---
# System prepended metadata

title: Sneaky VEH
tags: [CTF]

---

# Sneaky VEH
- Trước tiên ta chạy thử chall thì thấy cần truyền vào 4 tham số

![image](https://hackmd.io/_uploads/rkLPo9Z31x.png)
- Ta mở trong IDA thì thấy hàm main như sau

![image](https://hackmd.io/_uploads/SkH125Znkg.png)
- Ta thấy các argv sẽ được chuyển sang số ở hệ 16 và sẽ được lưu ở mảng `key`. Tiếp đó nó sẽ cấp phát một vùng nhớ và set giá trị bằng `memset`
- Ở đây ta chú ý vào các tham số ở hàm `VirtualAlloc` và mình sẽ xét một ví dụ và mở trong IDA

![image](https://hackmd.io/_uploads/rJgUTcbnyl.png)
```c=
#include<iostream>
#include<Windows.h>

using namespace std;

int main(){
    LPVOID lpAddress;
    lpAddress = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_GUARD | PAGE_READONLY);
    if (lpAddress == NULL){
        cout << "VirtualAlloc failed" << endl;
        return 1;
    }
    else{
        cout << "VirtualAlloc success" << endl;
    }
    memset(lpAddress, 0, 0x1000);    
    return 0;
}
```
![image](https://hackmd.io/_uploads/ByN2CqZ31g.png)
- Ta thấy `flProtect` là GUARD và READ. Điều này có nghĩa là vùng nhớ được cấp phát nếu truy cập vào thì sẽ raise ra một exception `STATUS_GUARD_PAGE_VIOLATION`. Nhưng khi ta debug trong IDA chỉ có exception `READONLY`

![image](https://hackmd.io/_uploads/Bkx6Jj-nJl.png)

- Có vẻ debugger đã loại bỏ exception. Đối với xử lý những bài exception đó ta phải đặt hardware bp ở exception thì debugger mới tiến hành nhảy tới, còn không đặt thì nó sẽ tự động bỏ qua và nhảy đến phần code tiếp theo.
- Và chúng ta có thể dễ dàng thấy phần xử lý exception đó

![image](https://hackmd.io/_uploads/rJDNbsZnkx.png)
- Khi Xref địa chỉ của các exception này ta có thể thấy rằng ở nằm trong struct của SEH

![image](https://hackmd.io/_uploads/ryoOZobnyg.png)
> Tham khảo [here](https://github.com/Speedi13/ManualMapped_SEH_32bit)
- Bây giờ ta sẽ đặt hardware bp ở exception đầu tiên thì chương trình đã raise được:

![image](https://hackmd.io/_uploads/SJIgZjWh1x.png)

- Trong hàm xử lý exception thì mình thấy input sẽ được xor với một đoạn shell và tiếp đó nó sẽ được thực thi

![image](https://hackmd.io/_uploads/BJxLmi-3Jg.png)
![image](https://hackmd.io/_uploads/SylzEi-2yx.png)
> Đây là đoạn sau khi giải mã
- Ta thấy rằng đoạn shell đó cuối cùng chỉ xor với một số từ 0 -> 255 nên ta có thể brute và xem thử đoạn code nào có khả năng đúng nhất
disasm.py
```py=
from capstone import *
from capstone.x86 import *

shell = [0xD4, 0x4D, 0x91, 0xFD, 0x7C, 0xB9, 0x28, 0x18, 0x18, 0x18, 
  0x93, 0x58, 0x14, 0x93, 0x58, 0x0C, 0x93, 0x18, 0x93, 0x58, 
  0x08, 0x45, 0xDB]
md = Cs(CS_ARCH_X86, CS_MODE_32)
md.detail = True
for key in range(0, 256):
    temp = shell[:]
    for i in range(len(temp)):
        temp[i] ^= key
    print("Key: %d" % key)
    for i in md.disasm(bytes(temp), 0x1000):
        print("0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))
```
- Mình nhận thấy tham số có thể là 0x18 và đây là đoạn code có khả năng đúng nhất

![image](https://hackmd.io/_uploads/BJpGrj-h1g.png)
- Và khi thực thi chương trình sẽ raise ra một exception tiếp

![image](https://hackmd.io/_uploads/Hy6kIsb31x.png)
- Và nó sẽ nhảy vào đây

![image](https://hackmd.io/_uploads/BJ0d8s-nyl.png)
- Sau đó nó tiếp tục giải mã đoạn shell thứ hai. Mình làm tương tự đoạn đầu tiên sẽ tìm được tham số thứ hai là 0xA. Tiếp đó chương trình thực thi đoạn shell thứ hai và raise ra một exception tương tự như cái đầu tiên (int 3)
- Và mình tiếp tục debug thì nó sẽ xor tham số thứ 3 với đoạn shell thứ 3. Sau đó nó sẽ nhảy đến đoạn try except thứ 3

![image](https://hackmd.io/_uploads/r1D_Oj-3yg.png)
- Trong lần call đầu tiên nó sẽ thực hiện resolve `RtlAddVectoredExceptionHandler` và thực thi với handler là `sub_7115D0`.
- Bây giờ đặt bp ở Handler_VEH để xem sau khi check exception xong thì chương trình làm gì ở đây.
- Ta thấy rằng các input sẽ được kiểm tra với cipher

![image](https://hackmd.io/_uploads/S1ac5oWnyl.png)

- Tiếp đến chương trình sử dụng kĩ thuật [here](https://anti-debug.checkpoint.com/techniques/assembly.html#instruction-counting)
- Ta để ý rằng sau khi thực hiện kiểm tra với cipher thì chương trình liên tục raise ra `exception Single step exception`. Và đây là nguyên nhân dẫn đến exception đó

![image](https://hackmd.io/_uploads/r1_-2i-3ye.png)
- Để bypass ta có thể dùng x32dbg loại bỏ exception đó bằng cách vào 
> option -> preferences -> Ignore Last
![image](https://hackmd.io/_uploads/S1oqAj-hye.png)
- Và ta thấy nó sẽ đi đến hàm này

![image](https://hackmd.io/_uploads/rkmox2b2Je.png)
- Ta có thể dùng frida để hook lấy tham số được truyền vào. Khi hook ta cần chú ý đến image base của chương trình, ta có thể dùng process hacker để quan sát nó.
hook.py
```py=
import frida
import sys
base = 0xbc0000
argv = ["sneaky_veh.exe","1c342e1e","b33390b","dc08050","3ef5b765"]
device = frida.get_local_device()
pid = device.spawn(argv)
session = device.attach(pid)
script = session.create_script("""
Interceptor.attach(ptr('0x00BC12A0'), { 
    onEnter: function(args) {
        var a1 = args[0]; 
        var z = Memory.readU64(a1.add(0));
        var x = Memory.readU32(a1.add(0));  
        var y = Memory.readU32(a1.add(4));  
        console.log("[+] x = 0x" + x.toString(16));
        console.log("[+] y = 0x" + y.toString(16));
        console.log("[+] z = 0x" + z.toString(16));
    }
});
""")

def on_message(message, data):
    print(message)

script.on('message', on_message)
script.load()

device.resume(pid)
sys.stdin.read()

# [+] x = 0x43534341
# [+] y=  0x34323032
# [+] z = 0x3432303243534341
```
solve.py
```py=
from z3 import *

key = [BitVec('key%d' % i, 32) for i in range(4)]
s = Solver()

cipher = [0x252D0D17, 0x253F1D15, 0xBEA57768, 0xBAA5756E]

for i in range(4):
    if i == 0:
        v7 = key[0]
        v5 = key[1]
    elif i == 1:
        v7 = key[1]
        v5 = key[0]
    elif i == 2:
        v7 = key[2]
        v5 = key[3]
    else:
        v7 = key[3]
        v5 = key[2]
    s.add((v5 ^ ((v7 << 16) | (v7 >> 8) & 0xFF00 | ((v7 >> 0x18)&0xff))) == cipher[i])
enc = [0x18,0xa]
for i in range(2):
    a = (key[i]) & 0xff
    b = (key[i] >> 8) & 0xff
    c = (key[i] >> 16) & 0xff
    d = (key[i] >> 24) & 0xff
    s.add((a ^ b ^ c ^ d) == enc[i])
s.add((key[0] ^ key[1]) == 0x43534341)
s.add((key[2] ^ key[3]) == 0x34323032)
s.add((key[1] ^ 0x43534341)&0xff == 0x99)
s.add((key[3] ^ 0x34323032)&0xff == 0x4f)
if s.check()  == sat:
    m = s.model()
    for i in range(4):
        print(hex(m[key[i]].as_long())[2:], end=' ')
# cfe7a999 8cb4ead8 15d89f4f 21eaaf7d 
```
- Flag:

![image](https://hackmd.io/_uploads/HJOwPdb2Jg.png)

