# PicoCTF - OTP Implementation
## Source code
:::spoiler IDA Main Function
```cpp
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // al
char v5; // dl
unsigned int v6; // eax
int i; // [rsp+18h] [rbp-E8h]
int j; // [rsp+1Ch] [rbp-E4h]
char input_key[112]; // [rsp+20h] [rbp-E0h] BYREF
char tmp_key[104]; // [rsp+90h] [rbp-70h] BYREF
unsigned __int64 v11; // [rsp+F8h] [rbp-8h]
v11 = __readfsqword(0x28u);
if ( argc > 1 )
{
strncpy(input_key, argv[1], 0x64uLL);
input_key[100] = 0;
for ( i = 0; valid_char(input_key[i]); ++i )// 確認字元是否在[0-9|a-f]之間
{
if ( i )
{
v4 = jumble(input_key[i]);
v5 = tmp_key[i - 1] + v4;
v6 = ((tmp_key[i - 1] + v4) >> 31) >> 28;
tmp_key[i] = ((v6 + v5) & 0xF) - v6;
}
else
{
tmp_key[0] = jumble(input_key[0]) % 16;
}
}
for ( j = 0; j < i; ++j )
tmp_key[j] += 0x61;
if ( i == 100
&& !strncmp(
tmp_key,
"bajbgfapbcclgoejgpakmdilalpomfdlkngkhaljlcpkjgndlgmpdgmnmepfikanepopbapfkdgleilhkfgilgabldofbcaedgfe",
100uLL) )
{
puts("You got the key, congrats! Now xor it with the flag!");
return 0;
}
else
{
puts("Invalid key!");
return 1;
}
}
else
{
printf("USAGE: %s [KEY]\n", *argv);
return 1;
}
}
```
:::
:::spoiler IDA Jumble Function
```cpp
__int64 __fastcall jumble(char input_key_char)
{
char v2; // [rsp+0h] [rbp-4h]
char v3; // [rsp+0h] [rbp-4h]
v2 = input_key_char;
if ( input_key_char > 0x60 )
v2 = input_key_char + 9;
v3 = 2 * (v2 % 16);
if ( v3 > 15 )
++v3;
return v3;
}
```
:::
:::spoiler IDA Valid Function
```cpp
_BOOL8 __fastcall valid_char(char a1)
{
if ( a1 > 0x2F && a1 <= 0x39 )
return 1LL;
return a1 > 0x60 && a1 <= 0x66;
}
```
:::
## Recon
這一題頗難,我寫的script也沒有很好,readability頗低,但我就爛,懶得優化了
1. 這一題簡單來說就是把我們輸入的key做一些操作,然後把它和`bajbgfa...`做比較,如果對了我們就可以直接和他提供的flag進行xor,然後轉換成ASCII
2. 有幾個重點,首先透過valid_char function可以知道我們輸入的key一定介於[0-9a-f]之間(這是個伏筆,因為他最後會直接和他提供的ciphertext進行xor,所以其實就是hex字元)
3. 接著可以從後面推回來,第一個flag做了一些操作,之後就直接加上0x61,再和`bajbgfa...`做比較,所以我們先減回去
:::spoiler tmp_key
`[1, 0, 9, 1, 6, 5, 0, 15, 1, 2, 2, 11, 6, 14, 4, 9, 6, 15, 0, 10, 12, 3, 8, 11, 0, 11, 15, 14, 12, 5, 3, 11, 10, 13, 6, 10, 7, 0, 11, 9, 11, 2, 15, 10, 9, 6, 13, 3, 11, 6, 12, 15, 3, 6, 12, 13, 12, 4, 15, 5, 8, 10, 0, 13, 4, 15, 14, 15, 1, 0, 15, 5, 10, 3, 6, 11, 4, 8, 11, 7, 10, 5, 6, 8, 11, 6, 0, 1, 11, 3, 14, 5, 1, 2, 0, 4, 3, 6, 5, 4]`
:::
4. 接著我們分析jumble function在幹嘛,簡單來說,如果傳入的是
* '0'-'9'$\to$return 0 2 4 6 8 10 12 14 17 19
* 'a'-'f'$\to$return 21 23 25 27 29 31
5. 跟著if statement走`tmp_key[0] = jumble(input_key[0]) % 16;`
我們知道`tmp_key[0]=0x1`(因為key的第一個字元是`b`$\to$`0x62`,減掉`0x61=0x1`),所以仔細推敲jumble(input_key[0])的return value是`0 2 4 6 8 10 12 14 1 3 5 7 9 11 13 15`(對應到的是[0-9a-f]),所以代表0x1在經過mod運算是0x11(要+16),而正確的key就是對應到的8
6. 接著換下一個字元,先破哏,if statement裡面的v6基本上是零,畢竟右移那麼多次,也不知道作者設計這個有甚麼用,可能是混淆逆向的?!但反正
```
v4 -> jumble(tmp_key[i-1])([0 2 4 6 8 10 12 14 , .... , 31])
v5 -> [0-15]+v4
v6 -> v5 >> 59 (基本上是0)
tmp_key[i] = v5 & 0xf
```
可以看到tmp_key[i]是v5和0xf做and operation,意思是他只會保留一個byte的後四個bits,跟一下gdb會發現前四bits,有可能存在,所以我們可以透過v5-v4是正還是負判斷前四bits有沒有數值,舉個例子,tmp_key[1]是0,而要判斷v5是0x0還是0x10可以透過減掉tmp_key[0]=1來決定,當結果是負的,就要加0x10,所以v5是0x10,v4是0xf,此時會發現沒有相對應的數值可以轉換,因為jumble function的return value並不包含0xf,所以我們要再加上0x10=0x1f,因為其實v5真正的數值是0x20才對,有了v4=31,就可以知道key[1]='f'
6. 就這樣不斷做下去,就能拿到key了,開寫script
## Exploit
```python
enc_key = "bajbgfapbcclgoejgpakmdilalpomfdlkngkhaljlcpkjgndlgmpdgmnmepfikanepopbapfkdgleilhkfgilgabldofbcaedgfe"
enc_key_1 = []
jumble_table = {
0:{'0':'0'},
2:{'2':'1'},
4:{'4':'2'},
6:{'6':'3'},
8:{'8':'4'},
10:{'10':'5'},
12:{'12':'6'},
14:{'14':'7'},
1:{'17':'8'},
3:{'19':'9'},
5:{'21':'a'},
7:{'23':'b'},
9:{'25':'c'},
11:{'27':'d'},
13:{'29':'e'},
15:{'31':'f'},
}
FLAG= ""
def get_flag(str_1):
if str_1 % 2 == 0:
return jumble_table[str_1][str(str_1)]
else:
return jumble_table[str_1][str(str_1 + 16)]
for i, single_chr in enumerate(enc_key):
enc_key_1.append(ord(single_chr) - 0x61)
if i == 0:
FLAG += get_flag(enc_key_1[-1])
else:
tmp = enc_key_1[-1] - enc_key_1[-2]
if tmp < 0:
tmp += 16
FLAG += get_flag(tmp)
cipher_text = open("./flag.txt", "r").read()
xor_tmp = int(cipher_text, 16) ^ int(FLAG, 16)
print(bytes.fromhex('{:x}'.format(xor_tmp)).decode('utf-8'))
```
Flag: `picoCTF{cust0m_jumbl3s_4r3nt_4_g0Od_1d3A_42dad069}`