cdxy.me
Footprints on Cyber Security and Python

环境

XCTF Final 和 HITB 赶在了周四周五,周四晚上拿到题目,此时队友已经对PHP代码完成了解密工作。

  • 解密后的代码: http://static.cdxy.me/DECODED.zip
  • github:https://github.com/Xyntax/XCTF-2018-Final-WEB2-PUBG

解密后的代码读起来有点麻烦,并无大碍。

png

有点魔幻的get flag过程

当晚并没有找到突破口,但发现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,需要满足两个条件:

  1. 绕过admin权限验证
  2. 能控制curl部分的回显

周四当晚并没有突破。周五早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就出来了....

png

然后提交拿了2血,几秒之后看到De1ta也交了flag,感谢De1ta的WEB大佬们送的火箭!

解题过程

当天下午完整打通了exp过程,分为三个部分:

  1. 找到注入点,偷数据
  2. 构造cookie,拿到admin权限
  3. 通过更新功能写shell到本地,读flag

SQL注入

CMS对SQL注入的防御策略

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敏感)。

正则写的没啥问题,接下来两个方向:

  1. 找到忘记使用过滤器直接传参的场景
  2. 找到外部没用'包裹的拼接,构造注入

注入构造

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�."'" );

这里因为没有过滤,可以'闭合然后构造一个布尔盲注。

png

png

Cookie构造

在使用admin_upload.php写shell之前,有权限校验,可使用从数据库中注出的数据,按源码的验证逻辑构造出cookie,拿到admin权限。

cookie构造逻辑在kss_inc/db_function.php line 300。

有两个k-v需要构造,下图红框部分为注入跑出来的数据,蓝框部分从源码配置文件里拿到:

png

webshell写入

回到最开始提到的从远程服务器更新代码的逻辑:

$_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植入。

主站回显构造:

png

Exp构造(带上之前构造好的cookie):

png

最终将可控回显写入_webup.php

png

然后通过webshell读到C盘根目录下的flag文件。


  • 给 @r3kapig 队友递茶
  • 给提供思路的队内WEB大佬递茶 @麦香 @zzm @hear7v @lynahex @n0b0dy
  • 给本题做的比我们快的 @Dubhe 和 @De1ta 两队WEB大佬递茶
  • 给出题人递茶 @RicterZ

png