---
# System prepended metadata

title: HW 0x01 Write Up
tags: [Course, CS, NCTU, SecureProgramming]

---

###### tags: `NCTU` `CS` `Course` `SecureProgramming`

HW 0x01 Write Up
==

## [Lab 1-1] pwntools practice
flag: FLAG{pwntools_is_not_that_hard}

題目要求是做100次讀入100個數字後排序並回傳

可以使用 python pwntools 來達成自動化

`r = remote('csie.ctf.tw', 10123)` 建立連線

`r.recvuntil("left\n")` 可以讀取到數字前一行

`r.recvline()` 將整行數字讀入

`r.sendline(payload)` 將排序過的數字轉成字串後回傳

## [Lab 1-2] orw
flag: FLAG{N0w_Y0u_le4rn4d_h0w_T0_she11c0de}

題目是要傳入 shellcode 讀取 `/home/orw/flag` 得到 flag

但是限制 system call 只能使用 open read write

所以就需建構如下的程式

```
int fd = open("/home/orw/flag");
char buf[40];
read(fd, buf, 40);
write(0, buf, 40);
```

而且為了做成 shellcode 需要轉成 asm

呼叫 syscall 時，使用 `rax` 來指定要執行的 syscall

參數需要照 `rdi` `rsi` `rdx` `r10` `r8` `r9` 的順序擺放

回傳值會放在 `rax`

|rax|syscall|rdi|rsi|rdx|
|--|--|--|--|--|
|0|read|fd|buf|size|
|1|write|fd|buf|size|
|2|open|filename|flags|mode|


使用的 stack 如圖
```
+--------------+ HIGH
+  w/flag\0\0  +
+--------------+
+   /home/or   +
+--------------+
+ buf(40bytes) +
+--------------+ LOW <- rsp
```
stack 是由 high address 往 low address 使用的

所以在將 path 放入 stack 時，需要分成 8bytes 一組，並且要從較後面的組堆疊下來

## [HW 0x01] short shell
flag: FLAG{r34d_4RE_th3_es1esT}

這個程式會從 stdin 讀取 10 bytes 並執行

但是執行 `exec("/bin/sh")` shellcode 大於 10 bytes
無法直接傳入

所以可以先建構一個 10 bytes 只做 read syscall 的 shellcode

將真正的 shellcode 讀到接下來執行的 stack 上

就可以繞過只能讀取 10 bytes 的限制了

在 call rdx 之前的 register 值

|rax|rdi|rsi|rdx|
|--|--|--|--|
|0|0x7b||shellcode最底下|

在 read syscall 之前 register 需要改成

|rax|rdi|rsi|rdx|
|--|--|--|--|
|0|0|原rdx + 10|超過`len(shellcode)`|

要先把 `rdx` 值複製到 `rsi` 上再讓 `rsi` 加 10，讓下一個 shellcode 連在 read syscall 後面，讀入完後直接執行 

`rdx` 需要一個超過 `len(shellcode)` 的值，原本 rdi 的值非常適合，所以就把 `rdi` 值複製到 `rdx`

`rdi` 需要設為 0，所以就把 `rax` 值複製到 `rdi` 上就可以了

因為只能用 10 bytes 的限制

所以有很多 opcode 使用空間太多而無法使用

要找可以做到相同功能但使用較少空間的指令取代

e.g.

```
mov rsi, rdx
=>
push rdx
pop rsi
```

使用的 stack 如圖

```
+----------------+
+ exec shellcode +
+----------------+ <- rsi
+ read shellcode +
+----------------+ <- 原本的 rdx
```

## [HW 0x01] shellsort2.0
flag: FLAG{SUPERB_sh311c0d1ng_ski115!!!}

這個程式會從 stdin 讀取 10000 bytes 以降序排序後再執行

所以只能建構原本就是降序的程式才會照著原本的功能執行

因為建構符合上面條件的 `exec("/bin/sh")` shellcode 過於困難

所以也是建構一個只做 read syscall 的 shellcode，將真正的 shellcode 讀到接下來執行的 stack 上

在 `call rdx` 之前的 register 值

|rax|rdi|rsi|rdx|
|--|--|--|--|
|0|0|0|shellcode最底下|

在 read syscall 之前 register 需要改成

|rax|rdi|rsi|rdx|
|--|--|--|--|
|0|0|rdx|超過`len(shellcode)`但不能超過 2^31|

所以要做的事情有兩個

1. `mov rsi, rdx`
2. 減少 `rdx` 的值

在大量嘗試後發現可以的指令順序是
```
nop
pop
push
xor rsi, [rdx]
```

所以可以使用多次固定次數 `pop` + 一次 `push rdx` 做到 `*rdx = rdx` 再接上 `rsi = *rdx` 來達成原本的 `mov rsi, rdx`

因為 `rdx` 指的位址就是 shellcode 的開頭，然而這個作法會把 shellcode 拔掉 8 bytes，所以要在前面放 8 個 `nop`

減少 `rdx` 的值的方法也是在大量嘗試後發現可以的指令是
```
sub edx, []
```

因為直接修改 `edx` 可以讓 high bits 直接歸 0，就可以達到 `rdx < 2^32`

使用 gdb 觀察後發現 `rcx` 通常會是指到 stack 上的記憶體，所以可以拿來當減數

這個做法就有一定機率可以成功

最後在傳入 exec shellcode 時 `rip > rsi`，這時只要在 shellcode 前加一堆 `nop` 就可以堆到 `rip` 後面，讓 shellcode 可以順利執行了

使用的 stack 如圖

執行 `call rdx` 前
```
+----------------+ 
+ read shellcode +
+----------------+
+ nop * 8        +
+----------------+ <- rdx
+ main function  +
+ 的變數          +
+----------------+ <- rsp
```

執行所有的 `pop` 後
```
+----------------+ 
+ read shellcode +
+----------------+ <- rsp
+                +
+----------------+ <- rdx
```

`push rdx` 後
```
+----------------+ 
+ read shellcode +
+----------------+
+ rdx value      +
+----------------+ <- rdx rsp
```

read syscall 後
```
+----------------+ 
+ exec shellcode +
+----------------+
+ many nops      + <- rip
+----------------+ <- rdx rsp
```