WaniCTF 2024 WP

WaniCTF 2024 WP

Crypto

beginners_rsa

题目:

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

p = getPrime(64)
q = getPrime(64)
r = getPrime(64)
s = getPrime(64)
a = getPrime(64)
n = p*q*r*s*a
e = 0x10001

FLAG = b'FLAG{This_is_a_fake_flag}'
m = bytes_to_long(FLAG)
enc = pow(m, e, n)
print(f'n = {n}')
print(f'e = {e}')
print(f'enc = {enc}')

'''
n = 317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347
e = 65537
enc = 127075137729897107295787718796341877071536678034322988535029776806418266591167534816788125330265
'''

解题:

yafu 分解

1
yafu-x64.exe factor(317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347)

多因数求解

1
2
3
4
5
6
7
8
9
10
11
12
13
enc = 127075137729897107295787718796341877071536678034322988535029776806418266591167534816788125330265
n = 317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347
p = 11771834931016130837
q = 12109985960354612149
r = 9953162929836910171
s = 13079524394617385153
a = 17129880600534041513

phi = (p-1)*(q-1)*(r-1)*(s-1)*(a-1)
d = inverse(0x10001, phi)
m = pow(enc, d, n)
print(long_to_bytes(m).decode())
# FLAG{S0_3a5y_1254!!}

FLAG{S0_3a5y_1254!!}

beginners_aes

题目:

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
# https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html
import hashlib
from os import urandom

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

key = b'the_enc_key_is_'
iv = b'my_great_iv_is_'
key += urandom(1)
iv += urandom(1)

cipher = AES.new(key, AES.MODE_CBC, iv)
FLAG = b'FLAG{This_is_a_dummy_flag}'
flag_hash = hashlib.sha256(FLAG).hexdigest()
msg = pad(FLAG, 16)
enc = cipher.encrypt(msg)

print(f'enc = {enc}') # bytes object
print(f'flag_hash = {flag_hash}') # str object

'''
enc = b'\x16\x97,\xa7\xfb_\xf3\x15.\x87jKRaF&"\xb6\xc4x\xf4.K\xd77j\xe5MLI_y\xd96\xf1$\xc5\xa3\x03\x990Q^\xc0\x17M2\x18'
flag_hash = 6a96111d69e015a07e96dcd141d31e7fc81c4420dbbef75aef5201809093210e
'''

解题:

随机key只有1字节 可以直接爆破

1
2
3
4
5
6
7
8
9
10
11
12
enc = b'\x16\x97,\xa7\xfb_\xf3\x15.\x87jKRaF&"\xb6\xc4x\xf4.K\xd77j\xe5MLI_y\xd96\xf1$\xc5\xa3\x03\x990Q^\xc0\x17M2\x18'
flag_hash = '6a96111d69e015a07e96dcd141d31e7fc81c4420dbbef75aef5201809093210e'

for i in range(256):
for j in range(256):
key = b'the_enc_key_is_' + bytes([i])
iv = b'my_great_iv_is_' + bytes([j])
dec = AES.new(key, AES.MODE_CBC, iv).decrypt(enc)[:32]
if dec[:4] == b'FLAG' and hashlib.sha256(dec).hexdigest() == flag_hash:
print(f'dec = {dec}')
break
# dec = b'FLAG{7h3_f1r57_5t3p_t0_Crypt0!!}'

FLAG{7h3_f1r57_5t3p_t0_Crypt0!!}

replacement

题目:

链接:https://pan.baidu.com/s/1vGXTelu7VjI_F95Jh5IB_g?pwd=p68u

1
2
3
4
5
6
7
8
9
10
11
12
# from secret import cal
import hashlib
cal = 'FLAG{This_is_a_flag}'

enc = []
for char in cal:
x = ord(char)
x = hashlib.md5(str(x).encode()).hexdigest()
enc.append(int(x, 16))

with open('my_diary_11_8_Wednesday1.txt', 'w') as f:
f.write(str(enc))

解题:

简单的单字符hash加密,可以生成所有打印字符的加密结果 对比密文得到明文

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


def getHash(char):
x = ord(char)
x = hashlib.md5(str(x).encode()).hexdigest()
return int(x, 16)


chars = string.printable
cd = {}
for i in chars:
cd[getHash(i)] = i

with open('my_diary_11_8_Wednesday.txt', 'r') as f:
enc = eval(f.read())

for j in enc:
print(cd[j], end='')

'''
Wednesday, 11/8, clear skies. This morning, I had breakfast at my favorite cafe. Drinking the freshly brewed coffee and savoring the warm buttery toast is the best. Changing the subject, I received an email today with something rather peculiar in it. It contained a mysterious message that said "This is a secret code, so please don't tell anyone. FLAG{13epl4cem3nt}". How strange!

Gureisya
'''

FLAG{13epl4cem3nt}

Forensics

tiny_usb

题目:

链接:https://pan.baidu.com/s/1lwil5baYKU6tpwfcH53IjA?pwd=x2a6

解题:

双击打开既是FLAG

FLAG{hey_i_just_bought_a_usb}

Surveillance_of_sus

题目:

链接:https://pan.baidu.com/s/1Fx5VhBhwuYg27dSvCU1_Jg?pwd=w35v

解题:

查看文件头 搜索发现是 RDP Bitmap 缓存文件 记录有图片碎片

使用 工具提取 bmc-tools.py

1
python bmc-tools.py -s Cache_chal.bin -d ./

发现有flag 碎片 拼接 得到flag

FLAG{RDP_is_useful_yipeee}

codebreaker

题目:

链接:https://pan.baidu.com/s/1qm6bMckH3w40NZDiKxCxpA?pwd=cv2e

解题:

修复二维码

使用 Adobe Illustrator 工具 建立无色 29*29 的网格 大小与二维码一样 使用实时上色工具将对应位置填充白色

已知 29*29 的二维码的模板 固定位置的黑色 再把不是纯黑的格子填成白色 就能扫了

FLAG{How_scan-dalous}

题目:

我发现了一个未知文件,打开它后,它引起了一些奇怪的行为,所以我进行了内存转储!攻击是如何进行的?

注意:文件中有两个FLAG。以 FLAG{H 开头的不是正确答案。请提交以 FLAG{D. 开头的。

链接:https://pan.baidu.com/s/1osqkusTAG9H-C_65dUStxQ?pwd=rbki

解题:

使用 MemProcFS-Analyzer 打开

通过分析 在 浏览器记录(timeline_web.csv)中 发现异常

base64解码 得到flag

继续分析 找另一个flag

在文件列表中(files.csv) 发现异常文件

010 分析 发现base64 编码的内容

解码后发现下载了一个 msedge.exe 在文件列表找到这个exe

运行得到一个base64字符串

解码得到另一个flag

FLAG{Dayum_this_is_secret_file}

I_wanna_be_a_streamer

题目:

Sorry Mom, I’ll work as a streamer.Watch my stream once in a while.(H.264 is used for video encoding.)

链接:https://pan.baidu.com/s/1DIhEWvgQQOCooj-uzfXQog?pwd=dsdi

解题:

Wireshark 打开 参照 wireshark提取视频数据之RTP包中提取H264和H265 安装插件解密h.264 流量

FLAG{Th4nk_y0u_f0r_W4tching}

tiny_10px

题目:

链接:https://pan.baidu.com/s/1DN9P8rcWZCYaSHSMebiySA?pwd=jmxj

解题:

010 Editor 打开 尝试修改图片宽高 发现有效果 但是不知道正确的宽高是多少

写 python 脚本 爆破宽高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import shutil


def modify_file(file_path, position, new_bytes, new_file_path):
shutil.copy(file_path, new_file_path)
with open(new_file_path, 'r+b') as file:
file.seek(position)
file.write(new_bytes)


for i in range(5, 20):
for j in range(5, 20):
position = 0xd90
y = i * 10
sy = y.to_bytes(2, byteorder='big')
x = j * 10
sx = x.to_bytes(2, byteorder='big')
hex_value = sy + sx

file_path = r'chal_tiny_10px.jpg'
new_file_path = rf'imgs\tiny_{str(y)}_{str(x)}.jpg'

modify_file(file_path, position, hex_value, new_file_path)

宽度为 160 的可以正常显示

FLAG{b1g_en0ugh}

WEB

Bad_Worker 签到题

请求被 service worker 拦截修改了 直接 curl 请求真实 FLAG.txt 获得flag

FLAG{pr0gr3ssiv3_w3b_4pp_1s_us3fu1}

pow

题目:

解题:

查看代码发现逻辑 是 满足 hash() 时 post 当前字符串到 /api/pow progress 进度加1 要将进度达到 1000000 获得flag

测试接口 发现 在数组中重复元素 post 后 progress 会加上元素的数量 那就一次多提交点

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

cookie = requests.post('https://web-pow-ywu5dn.wanictf.org/api/pow', json=[]).headers['Set-Cookie'].split('=')[1]

while True:
cookies = {
'pow_session': cookie,
}

headers = {
'content-type': 'application/json',
}

json_data = ['2862152'] * 90000

response = requests.post('https://web-pow-ywu5dn.wanictf.org/api/pow', cookies=cookies, headers=headers, json=json_data)
print(response.text)
if 'progress' not in response.text:
break

#progress: 90000 / 1000000
#progress: 180000 / 1000000
#progress: 270000 / 1000000
#progress: 360000 / 1000000
#progress: 450000 / 1000000
#progress: 540000 / 1000000
#progress: 630000 / 1000000
#progress: 720000 / 1000000
#progress: 810000 / 1000000
#progress: 900000 / 1000000
#progress: 990000 / 1000000
#FLAG{N0nCE_reusE_i$_FUn}

FLAG{N0nCE_reusE_i$_FUn}

One Day One Letter

题目:

链接:https://pan.baidu.com/s/1V4JmW3kceqa5a9q8vpoWDQ?pwd=40zf

解题:

分析代码 逻辑为 content-server 向 time-server 请求 pubkey 用来验证做签名验证 并且根据 timestamp 的值计算返回FLAG 对应位置的值
由于 content-server 和 time-server 不是同一服务器 并且 content-server 可以任意指定 time-server 的地址

那我们可以自己搭一个 time-server 从而控制 timestamp 得到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
import json
import ssl
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, HTTPServer

from Crypto.Hash import SHA256
from Crypto.PublicKey import ECC
from Crypto.Signature import DSS

key = ECC.generate(curve='p256')
pubkey = key.public_key().export_key(format='PEM')


class HTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/pubkey':
self.send_response(HTTPStatus.OK)

self.send_header('Content-Type', 'text/plain; charset=utf-8')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
res_body = pubkey
self.wfile.write(res_body.encode('utf-8'))
self.requestline
else:
alltime = []
d = {}
start = 1719050000
for i in range(1, 50): #构造 12 个 timestamp
start = start + 3600 * i
x = start // (60 * 60 * 24) % 12
d[x] = start
for i in list(d.values()): # 获得 12 个 timestamp 和 signature
timestamp = str(int(i)).encode('utf-8')
h = SHA256.new(timestamp)
signer = DSS.new(key, 'fips-186-3')
signature = signer.sign(h)
alltime.append({'timestamp': timestamp.decode('utf-8'), 'signature': signature.hex()})
self.send_response(HTTPStatus.OK)
self.send_header('Content-Type', 'text/json; charset=utf-8')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()

res_body = json.dumps({'all': alltime})
self.wfile.write(res_body.encode('utf-8'))


handler = HTTPRequestHandler
httpd = HTTPServer(('', 443), handler)
httpd.socket = ssl.wrap_socket(httpd.socket, keyfile="server.key", certfile='server.crt', server_side=True)
httpd.serve_forever()

由于get_pubkey_of_timeserver() 限制了只能是 https 而且证书还得是安全的

使用 Let’s Encrypt 生成证书

服务运行后 访问 /pubkey 可以得到自己的 pubkey 访问根 可以得到 12 和 timestamp 和 signature

获取flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

r = requests.get('https://xxxxxxxxxxxxxxx/')
ts = r.json()

for i in ts['all']:
json_data = {
'timestamp': i['timestamp'],
'signature': i['signature'],
'timeserver': 'xxxxxxxxxxxxxxx',
}

response = requests.post('https://web-one-day-one-letter-content-lz56g6.wanictf.org/', json=json_data)
print(response.text.split('\n')[1])

# FLAG{lyingthetime}

FLAG{lyingthetime}

Reversing

lambda

题目:

1
2
3
4
5
6
import sys

sys.setrecursionlimit(10000000)

(lambda _0: _0(input))(lambda _1: (lambda _2: _2('Enter the flag: '))(lambda _3: (lambda _4: _4(_1(_3)))(lambda _5: (lambda _6: _6(''.join))(lambda _7: (lambda _8: _8(lambda _9: _7((chr(ord(c) + 12) for c in _9))))(lambda _10: (lambda _11: _11(''.join))(lambda _12: (lambda _13: _13((chr(ord(c) - 3) for c in _10(_5))))(lambda _14: (lambda _15: _15(_12(_14)))(lambda _16: (lambda _17: _17(''.join))(lambda _18: (lambda _19: _19(lambda _20: _18((chr(123 ^ ord(c)) for c in _20))))(lambda _21: (lambda _22: _22(''.join))(lambda _23: (lambda _24: _24((_21(c) for c in _16)))(lambda _25: (lambda _26: _26(_23(_25)))(lambda _27: (lambda _28: _28('16_10_13_x_6t_4_1o_9_1j_7_9_1j_1o_3_6_c_1o_6r'))(lambda _29: (lambda _30: _30(''.join))(lambda _31: (lambda _32: _32((chr(int(c,36) + 10) for c in _29.split('_'))))(lambda _33: (lambda _34: _34(_31(_33)))(lambda _35: (lambda _36: _36(lambda _37: lambda _38: _37 == _38))(lambda _39: (lambda _40: _40(print))(lambda _41: (lambda _42: _42(_39))(lambda _43: (lambda _44: _44(_27))(lambda _45: (lambda _46: _46(_43(_45)))(lambda _47: (lambda _48: _48(_35))(lambda _49: (lambda _50: _50(_47(_49)))(lambda _51: (lambda _52: _52('Correct FLAG!'))(lambda _53: (lambda _54: _54('Incorrect'))(lambda _55: (lambda _56: _56(_41(_53 if _51 else _55)))(lambda _57: lambda _58: _58)))))))))))))))))))))))))))

解题:

提取几处for循环关键代码 测试

逻辑为

1
2
3
4
5
6
7
t1 = chr(ord("F") + 12)
t2 = chr(ord(t1) - 3)
t3 = chr(123 ^ ord(t2))

c = chr(int("16", 36) + 10)

t3==C => True

依次爆破每个字符 得到flag

1
2
3
4
5
6
7
8
9
10
11
import string

c = '16_10_13_x_6t_4_1o_9_1j_7_9_1j_1o_3_6_c_1o_6r'
e = [chr(int(i, 36) + 10) for i in c.split('_')]

for x in range(len(e)):
for m in string.printable:
if chr(123 ^ ord(chr(ord(chr(ord(m) + 12)) - 3))) == e[x]:
print(m, end='')
break
#FLAG{l4_1a_14mbd4}

FLAG{l4_1a_14mbd4}

home

题目:

链接:https://pan.baidu.com/s/1x_B4kvGAcBCteSwzoohQxw?pwd=js39

解题:

IDA pro 64 打开 分析伪代码

关键函数 constructFlag() 对 byte_2010 做了复杂的转换 最后的结果可能是flag

main 函数有调试检查 可以绕过; 还有路径检查 需要放在 Service 文件件下运行

使用pwngdb 打开 在 main 和 constructFlag 函数下断点 运行后 跳转到 constructFlag 函数 然后 重复步过执行 观察栈中的值

1
2
3
4
5
6
7
8
gdb chal_home  			# 打开GBD 调试
b main # main 函数下断点
b constructFlag # constructFlag 函数下断点
r # 运行程序
jump constructFlag # 跳转到 constructFlag 函数
set context-stack-lines 50 # 设置STACK显示50行
n 5000 # 重复执行 步过 直到 FLAG 完整显示
...

FLAG{How_did_you_get_here_4VKzTLibQmPaBZY4}

Thread

题目:

链接:https://pan.baidu.com/s/1ZFEzA3w7tXmXm-ng9ig6pw?pwd=znsn

解题:

IDA pro 64 打开 分析伪代码

使用Dev-C++还原逻辑 调试运行发现 可以逐字 爆破

编写爆破代码

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

pthread_mutex_t mutex;
int dword_4140[45];
int dword_4020[45] = {0xA8, 0x8A, 0xBF, 0xA5, 0x2FD, 0x59, 0xDE, 0x24, 0x65, 0x10F, 0xDE, 0x23, 0x15D, 0x42, 0x2C, 0xDE, 0x9, 0x65, 0xDE, 0x51, 0xEF, 0x13F, 0x24, 0x53, 0x15D, 0x48, 0x53, 0xDE, 0x9, 0x53, 0x14B, 0x24, 0x65, 0xDE, 0x36, 0x53, 0x15D, 0x12, 0x4A, 0x124, 0x3F, 0x5F, 0x14E, 0xD5, 0xB};
int dword_4200[45];

void *start_routine(void *arg) {
int index = *(int *)arg;
pthread_mutex_lock(&mutex);
int count = dword_4200[index];
while (count <= 2) {
int operation = (dword_4200[index] + index) % 3;
if (operation == 0)
dword_4140[index] *= 3;
else if (operation == 1)
dword_4140[index] += 5;
else if (operation == 2)
dword_4140[index] ^= 0x7F;

count = ++dword_4200[index];
}
pthread_mutex_unlock(&mutex);
return NULL;
}

int main() {
char cc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_{}";
char s[46] = "";
pthread_t th[45];
int v8[45];

pthread_mutex_init(&mutex, NULL);

for (int c = 0; c < 45; ++c) {
for (int d = 0; d < 65; ++d) {
s[c] = cc[d];

for (int i = 0; i <= 44; ++i) {
dword_4140[i] = s[i];
dword_4200[i] = 0; //需要重置
}

for (int j = 0; j <= 44; ++j) {
v8[j] = j;
pthread_create(&th[j], NULL, start_routine, &v8[j]);
}

for (int k = 0; k <= 44; ++k) {
pthread_join(th[k], NULL);
}


if (dword_4140[c] == dword_4020[c]) {
printf("%s\n", s);
break;
}
}
}

pthread_mutex_destroy(&mutex);
return 0;
}

FLAG{c4n_y0u_dr4w_4_1ine_be4ween_4he_thread3}

Pwnable

nc 签到题

链接:https://pan.baidu.com/s/1eWoGlIodMhlxHrsyLxEgeQ?pwd=jbaa