使用objcopy选择性修改删除符号表以及其他相关elf段以及gdb debug file 任意读漏洞以及xctf mamadebug writeup

21 篇文章 2 订阅
本文讲述了如何通过objcopy工具操作elf文件,将调试信息和符号表分离,并利用gdb的漏洞在xctf竞赛中修改debug文件实现flag获取。涉及的技术包括objcopy、dwarf段、debug文件校验和路径替换技巧。
摘要由CSDN通过智能技术生成

由于一个elf文件里面的调试信息和符号信息占用了很大的空间,而这些信息只是调试的时候才用到,平时运行的时候用不到。所以我们可以把这部分信息单独dump出来作为一个独立的文件。开发者在需要调试的时候可以下载这个文件来恢复elf的调试信息和符号表。

objcopy --only-keep-debug foo foo.debug
#将elf文件的调试信息单独dump出来作为一个独立的elf文件foo.debug

但是怎么知道这个elf文件的debug文件是什么呢?这就要参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html

根据这篇文章,elf文件对应的debug文件在.gnu_debuglink段里面。分别指定了文件名和文件的crc.
我们可以dump出.gnu_debuglink段来查看:

objcopy —dump-section .gnu_debuglink=mama2   hello
#dump某个elf段到某个文件中,比如上述例子是把.gnu_debuglink段dunmp到mama2这个文件中

在这里插入图片描述
可以看到debug文件的文件名是hello.debug,其crc32是94 31 F1 9E,由于是小端序的,所以crc值是0x9EF13194。
如果想更换debug文件,就要删除.gnu_debuglink段然后重新添加:

objcopy --remove-section=.gnu_debuglink  hello #删除某个elf段,比如.gnu_debuglink段
objcopy --add-gnu-debuglink=foo.debug hello
#添加了foo.debug作为调试文件。

接下来我们来看xctf的一道题:

import functools
import sys
import tempfile
import shutil
import os
import subprocess

print = functools.partial(print, flush=True)

def main():
    print('What do you think should be present in a debug info file ?')
    print('Give me your answer, I will debug it for you :)')
    print('File > ')

    content = sys.stdin.buffer.read(0x1024)
    print(len(content))
    with tempfile.TemporaryDirectory() as tempdir:
        bin_path = os.path.join(tempdir, 'hello')
        shutil.copyfile('/home/ctf/hello', bin_path)
        with open(os.path.join(tempdir, 'hello.debug'), 'wb') as f:
            f.write(content)
        os.chdir(tempdir) 
        os.chmod(bin_path, 0o755)
        subprocess.run(['gdb', 'hello','-ex','set confirm off', '-ex', 'b main', '-ex', 'r', '-ex', 'p a', '-ex', 'c','-ex','q'])
        

if __name__ == '__main__':
    main()

这道题首先限制了debug文件的大小要小于4k。其次,这道题会将你输入的文件作为debug文件进行调试。同时也会校验你debug文件与hello文件里面指定的debug文件的crc是否相等。

这道题的正确解法是这样的:
首先我们创建一个c语言文件,不引入任何头文件,为了确保足够小:

int main(){
	return 0;
}

如果我们在gcc编译的时候加上-g参数,

gcc -g test.c -o mama

那么断点断在main函数的时候是这样的:

在这里插入图片描述

可以看到,gdb在加上-g参数的时候断点的时候会自动打印断点所在的源码。这是为什么呢,因为gdb 加上-g的时候编译,编译出来的elf文件会多一些段,我们成为dwarf段:

详见:https://blog.csdn.net/JS072110/article/details/44153303

我们使用readelf -S elf可以查看这些段。

在这里插入图片描述
这些带有.debug的段就是dwarf段。

那么这个时候,我们拔test.c删掉再次断在main函数会怎么样呢?

在这里插入图片描述
gdb会提示找不到文件,这就说明gdb在读取程序源码文件的时候是根据dwarf文件的信息从外部读取文件的。这给我们利用gdb的任意读漏洞埋下了伏笔。

既然原文件名称的信息是存在dwarf文件里的,那么我们把这个文件名改为 “/flag” 会怎么样呢?岂不是就读取了题目的flag文件?

那么,如何修改elf文件里面的dwarf信息呢?这里用到了个小技巧,就是字符串替换,既然"/flag"这样一个带根目录的文件名是五个字符,那么我们也把原来的原文件命名为五个字符的源文件比如"nep.c",然后编译成功后提取出debug文件,然后使用16进制编辑器将“nep.c”替换为“/flag”会怎么样呢?
替换后一尝试,就发现读取出了flag:
在这里插入图片描述
如果文件名大于或者小于五个字符,那么由于修改dwarf信息会导致elf里面的偏移有所变化,会损坏elf文件。
当然了,这道题还有两个个比较严格的限制,
一个是debug文件必须与服务器上面正在调试的hello文件相同,这个可以用我的上一篇文章将的方法绕过:
https://blog.csdn.net/fjh1997/article/details/117415578

第二个就是
这个debug文件大小不能大于4k。由于即使一个很小的不带任何头文件的c语言编译出来的debug文件都是5,6k左右,所以我们要对elf文件进行瘦身。

经过研究,要使得这个debug文件生效,除了需要保留debug文件信息,也就是dwarf相关的段,还要保存符号表相关的段strtab .shstrtab(因为要保存main这个函数的符号表)以及.text数据段。

在这里插入图片描述

一开始我使用以下方法瘦身,虽然瘦了0.8k但还不够

objcopy --keep-symbol main --keep-symbol hello.c --strip-all   hello.debug hello2.debug #除了main函数以及源码文件hello.c的的符号表保留外,其余都删除,这样可以大大减少体积。

不如再彻底一点删掉所有不必要的数据段。

objcopy --keep-symbol main  --keep-section .debug* --keep-section .strtab --keep-section .shstrtab --keep-section .text    --only-section .symtab   nep.debug haha.debug 

使用这样的方法对elf文件进行瘦身,即可解出题目。

最后感谢T神(ThTsOd)传授的经验和套路,爱你~~~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值