原文受邀转发
@r3kapig 战队欢迎大佬入伙,邮件 i[at]cdxy.me
XCTF Final 和 HITB 赶在了周四周五,周四晚上拿到题目,此时队友已经对PHP代码完成了解密工作。
解密后的代码读起来有点麻烦,并无大碍。
当晚并没有找到突破口,但发现kss_admin/admin_update
函数疑似是exp链路中的一环。
其中120行发现CMS更新功能,从远端主站拉取代码写入本地:
$_obfuscate_koiKkIiPjI6UkYeRlIqNhoc� = _obfuscate_lY6Gk5KMkYmPjIyPhpCOlYc�( "http://api.hphu.com/import/".$_obfuscate_koaSiYqGjIqMiZSLk4uGiZU�.".php?phpver=".PHP_VERSION."&webid=".WEBID."&rid=".time( ), 300 );
跟进Yc
函数,发现其注册了两个curl的回调 read_header
,read_body
:
curl_setopt( $_obfuscate_joiNh4aIhouViZGQho_JiI4�, CURLOPT_HEADERFUNCTION, "read_header" );
curl_setopt( $_obfuscate_joiNh4aIhouViZGQho_JiI4�, CURLOPT_WRITEFUNCTION, "read_body" );
其中read_body
函数会将curl到的content写到本地文件kss_tool/_webup.php
file_put_contents( KSSROOTDIR."kss_tool".DIRECTORY_SEPARATOR."_webup.php", $_obfuscate_jJWMiJWJjoyIkYmLjY6VipM�, FILE_APPEND );
想要使用admin_upload.php
这个点写shell,需要满足两个条件:
周四当晚并没有突破。周五早9点比赛环境恢复,我打开ipython对这个疑似webshell的地址kss_tool/_webup.php
做了监控。如果这个文件被其他队动了,就证明这个思路是可行的。
In [1]: while True:
...: try:
...: r = requests.get('http://guaika.txmeili.com:8888/kss_tool/_webup.php')
...: except Exception,e:
...: print e
...: continue
...: if r.content not in ans:
...: print r.content
...: ans.append(r.content)
结果发现这个文件的http response在一直变化,看着看着flag就出来了....
然后提交拿了2血,几秒之后看到De1ta也交了flag,感谢De1ta的WEB大佬们送的火箭!
当天下午完整打通了exp过程,分为三个部分:
kss_inc/function
github link
实现了多种过滤方案,然后在SQL语句拼接取参时,通过传入参数指定取参的位置(GET/POST/COOKIE)和过滤方案(sql/sqljs/num等)
外部取参的代码示例:
$_obfuscate_iJWMjIiVi5OGjJOViY2Li48� = _obfuscate_i4mIkpOGkomKiouRhoaMh5I�( "out_trade_no", "pg", "sql", "" );
意思是从POST/GET(pg)中取出参数out_trade_no
的值,然后通过sql
过滤器的检查后,赋值到i48
变量。
先简单看下过滤器的正则:
case "sql" :
if ( preg_match( "/select|insert|update|delete |union|into|load_file|outfile|char|0x[0-9a-f]{6}|\\.\\/|\\/\\*|'/i", $_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙ ) )
{
ob_clean( );
$_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙ = preg_replace( "/(select|insert|update|delete |union|into|load_file|outfile|char|0x[0-9a-f]{6}|\\.\\/|\\*|')/i", "<font color=red>$1</font>", $_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙ );
exit( "<p>MySQL injection:".$_obfuscate_lIyOioeNkY6Vj4qPkJGMiJQ˙.",".$_obfuscate_iYyTho_HlJCOh4yRj4ePj4k˙.",".$_obfuscate_ipCJlJOSlJSQkYqNlYqKlIs˙."</p>" );
过滤了'
,然后匹配到这些危险字符时,会将参数带到html回显,使response可控(XSS敏感)。
正则写的没啥问题,接下来两个方向:
'
包裹的拼接,构造注入kss_inc/payapi_return2.php
是一个外部支付功能。其中的chinabank
,e138
两种支付方式均存在"未使用过滤器"直接传参的漏洞。
else if ( $_obfuscate_kYyPkY_PkJKVh4qGjJGIio4� == "chinabank" )
{
$_obfuscate_kpGPh4mNh46SkZONh4eLlJU� = "";
$_obfuscate_k42NkY2RkoiNjJCKlZSKiIg� = trim( $_POST['v_oid'] );
$_obfuscate_iJWMjIiVi5OGjJOViY2Li48� = $_obfuscate_k42NkY2RkoiNjJCKlZSKiIg�;
$_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� = trim( $_POST['v_pstatus'] );
$_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU� = trim( $_POST['v_amount'] );
$_obfuscate_lIuQk5OGjpKVjY6UiI_QjJM� = $_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU�;
$_obfuscate_hpCRlJCSjI6Ki5WSipCLkpQ� = trim( $_POST['v_moneytype'] );
$_obfuscate_lJSPjJCOi5CIiJSSkZWNh4Y� = trim( $_POST['remark1'] );
$_obfuscate_iImJjYmQjYyOjIuVkIuMjIs� = trim( $_POST['v_md5str'] );
if ( $_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� == "20" )
{
$_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "TRADE_FINISHED";
}
else
{
$_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "WAIT_BUYER_PAY";
}
}
else if ( $_obfuscate_kYyPkY_PkJKVh4qGjJGIio4� == "e138" )
{
$_obfuscate_kpGPh4mNh46SkZONh4eLlJU� = "";
$_obfuscate_k42NkY2RkoiNjJCKlZSKiIg� = trim( $_POST['SerialNo'] );
$_obfuscate_iJWMjIiVi5OGjJOViY2Li48� = $_obfuscate_k42NkY2RkoiNjJCKlZSKiIg�;
$_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� = trim( $_POST['Status'] );
$_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU� = trim( $_POST['Money'] );
$_obfuscate_lIuQk5OGjpKVjY6UiI_QjJM� = $_obfuscate_jpGJk5SSkJOIk4iQiI_OhpU�;
$_obfuscate_iImJjYmQjYyOjIuVkIuMjIs� = trim( $_POST['VerifyString'] );
if ( $_obfuscate_iIuQkYaUioqGlI6IjIuMiI8� == "2" )
{
$_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "TRADE_FINISHED";
}
else
{
$_obfuscate_i5CMioaGiI6ShomNiIuKjJE� = "WAIT_BUYER_PAY";
}
}
SQL执行时GP
被带入i48
变量。
$_obfuscate_lZGQj4iOj4mTlZGNjZGUj5E� = $_obfuscate_jIaUiIeSjZWKlIqLkIqOioc�->_obfuscate_iY6OkJCRkY2PjpCPk5CRkJA�( "select * from kss_tb_order where ordernum='".$_obfuscate_iJWMjIiVi5OGjJOViY2Li48�."'" );
这里因为没有过滤,可以'
闭合然后构造一个布尔盲注。
在使用admin_upload.php
写shell之前,有权限校验,可使用从数据库中注出的数据,按源码的验证逻辑构造出cookie,拿到admin权限。
cookie构造逻辑在kss_inc/db_function.php
line 300。
有两个k-v需要构造,下图红框部分为注入跑出来的数据,蓝框部分从源码配置文件里拿到:
回到最开始提到的从远程服务器更新代码的逻辑:
$_obfuscate_koiKkIiPjI6UkYeRlIqNhoc� = _obfuscate_lY6Gk5KMkYmPjIyPhpCOlYc�( "http://api.hphu.com/import/".$_obfuscate_koaSiYqGjIqMiZSLk4uGiZU�.".php?phpver=".PHP_VERSION."&webid=".WEBID."&rid=".time( ), 300 );
URL里面拼接的变量是外部可控的,我们在这个主站的test目录下发现了一套Demo的CMS:
http://api.hphu.com/test/kss_admin/index.php
回想之前的SQL注入过滤机制,我们可以触发这个机制,将php代码写入http回显,然后admin_upload.php
通过curl
读内容时会将页面中的php代码写入_webup.php
,完成webshell植入。
主站回显构造:
Exp构造(带上之前构造好的cookie):
最终将可控回显写入_webup.php
然后通过webshell读到C盘根目录下的flag文件。