allways r0ops !
只值 150 分的逆向題,卻在上面浪費了不少時間,覺得很挫敗...
r0ops: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xb16f0af4069e5f9972c2bbb85360187144163125, stripped
首先程式會 listen on 13337 port,接著執行位於 0xDEAD3AF
上的函數
要注意的是由於程式會直接判斷 fd 是否等於 3,如果用 debugger load 的話 fd 通常會大於 3, 需要手動 patch 一下
int main()
{
int fd = socket(2, 1, 0);
if ( fd == 3 ) {
addr.sa_family = 2;
addr.sa_data[2] = htonl(0);
addr.sa_data[0] = htons(13337);
bind(3, &addr, 16u);
listen(fd, 10);
sub_DEAD3AF();
}
}
int sub_DEAD3AF()
{
int fd = accept(3, 0, 0);
recv(fd, buffer, 4096, 0);
close(fd);
memcpy(0xE0AF0A0, 0xE0B00A0, 4096);
__asm {
mov eax, offset buffer
mov rdi, rax
mov eax, 0x0B20C0B8
mov rsi, rax
mov eax, 0xE0AF8A0
mov rsp, rax
ret
}
}
sub_DEAD3AF()
會 recv 4096 bytes,最後把 rsp 修改為 0xE0AF8A0
後執行 ret
如同題目名稱,rsp 指向的資料區塊是個 rop chain, 透過肉眼辨識配合 IDA Pro 重新命名可以很快的把這些 function address 整理成對應的指令
接著轉成類 asm 格式:
8
pop r9
1337DEADBEEF0095h
mov rax, [rdi]
add rsi, 8
mov [rsi], rax
mov r8, [rsi]
sub rsi, 8
add rdi, 8
add rsi, 8
mov [rsi], r9
mov rax, [rsi]
sub rsi, 8
pop rbx
0CAFEh
imul rax, rbx
add rsi, 8
mov [rsi], rax
mov r9, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r9
mov rax, [rsi]
sub rsi, 8
pop rbx
0BEEFh
add_rax_rbx
add rsi,
mov [rsi], rax
mov r9, [rsi]
sub rsi, 8
pop r12
1
pop r10
3419h
pop rax
0
pop rbx
0
pop rdx
1D8h
cmp rax, rbx | jz +rdx
add rsi, 8
mov [rsi], r10
mov r11, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r11
mov rax, [rsi]
sub rsi, 8
pop rbx
1
and rax, rbx
add rsi, 8
mov [rsi], rax
mov r11, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r11
mov rax, [rsi]
sub rsi, 8
pop rbx
1
pop rdx
68h
cmp rax, rbx | jne +rdx
add rsi, 8
mov [rsi], r12
mov rax, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r8
mov rbx, [rsi]
sub rsi, 8
imul rax, rbx
add rsi, 8
mov [rsi], rax
mov r12, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r8
mov rax, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r8
mov rbx, [rsi]
sub rsi, 8
imul rax, rbx
add rsi, 8
mov [rsi], rax
mov r8, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r10
mov rax, [rsi]
sub rsi, 8
shr rax, 1
add rsi, 8
mov [rsi], rax
mov r10, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r10
mov rax, [rsi]
sub rsi, 8
pop rbx
0
pop rdx
0FFFFFFFFFFFFFDE0h
cmp rax, rbx | jne +rdx
add rsi, 8
mov [rsi], r12
mov rax, [rsi]
sub rsi, 8
add rsi, 8
mov [rsi], r9
mov rbx, [rsi]
sub rsi, 8
pop rdx
20h
cmp rax, rbx | jne +rdx
pop rdx
0FFFFFFFFFFFFFC38h
add rap, rdx
call PrintFlag
call sub_DEAD3AF
不難發現 register 之間的資料轉移都是位於 add rsi, 8
與 sub rsi, 8
之間, 可以再進一步簡化翻譯出 C code:
#include <stdio.h>;
unsigned long long flag[8] = {1, 1, 1, 1, 1, 1, 1, 1};
void PrintFlag() {
puts("YOU WIN!\n");
printf("FLAG IS: 0ctf{");
for ( int i = 0; i < 8; ++i )
printf("%08llx", flag[i]);
puts("}\n");
}
int main( int argc, char *argv[] )
{
unsigned long long r9 = 0x1337DEADBEEF0095;
for ( int i = 0; i < 8; ++i ) {
unsigned long long r8 = flag[i];
unsigned long long r12 = 1;
unsigned long long r10 = 13337;
r9 *= 0xCAFE;
r9 += 0xBEEF;
while ( r10 ) {
if ( r10 & 1 )
r12 *= r8;
r8 *= r8;
r10 >>= 1;
}
if ( r12 != r9 )
printf("Failed.\n");
}
PrintFlag();
}
// r9 in 8 rounds:
// 0x2724090c0798e4c5
// 0x44e477ee2e372c65
// 0xa150eec963c67d25
// 0xeab7d48b9db01ba5
// 0xf01b0cf36a8c5ea5
// 0x930eeb9679f4d8a5
// 0xaeb27b8833e1e4a5
// 0x2a900a13b88bcca5
整理一下整個流程,程式會從 port 13337 讀取 8 個 64bits 整數, 計算出每個數的 13337 次方 mod 2 ^ 64 並與特定值比較,最後用這符合條件的 8 個數拼出 flag
由於程式最後組 flag 時是用 "%08llx"
做 format,因此只需要知道目標數的最後 4 bytes, 可以直接用暴力破解的方式得到符合條件的值:
#include <stdio.h>
void f(unsigned long long r8)
{
unsigned long long tmp = r8;
unsigned long long r9 = 0x1337DEADBEEF0095;
unsigned long long r12 = 1;
unsigned long long r10 = 13337;
r9 *= 0xCAFE;
r9 += 0xBEEF;
while ( r10 ) {
if ( r10 & 1 )
r12 *= r8;
r8 *= r8;
r10 >>= 1;
}
if ( ( r12 & 0xFFFFFFFF ) == 0x0798e4c5 )
printf("Flag1 Found! %llx\n", tmp);
else if ( ( r12 & 0xFFFFFFFF ) == 0x2e372c65 )
printf("Flag2 Found! %llx\n", tmp);
else if ( ( r12 & 0xFFFFFFFF ) == 0x63c67d25 )
printf("Flag3 Found! %llx\n", tmp);
else if ( ( r12 & 0xFFFFFFFF ) == 0x9db01ba5 )
printf("Flag4 Found! %llx\n", tmp);
else if ( ( r12 & 0xFFFFFFFF ) == 0x6a8c5ea5 )
printf("Flag5 Found! %llx\n", tmp);
else if ( ( r12 & 0xFFFFFFFF ) == 0x79f4d8a5 )
printf("Flag6 Found! %llx\n", tmp);
else if ( ( r12 & 0xFFFFFFFF ) == 0x33e1e4a5 )
printf("Flag7 Found! %llx\n", tmp);
else if ( ( r12 & 0xFFFFFFFF ) == 0xb88bcca5 )
printf("Flag8 Found! %llx\n", tmp);
}
int main( int argc, char *argv[] )
{
for ( unsigned int i = 0; i < 0xFFFFFFFF; ++i )
f(i);
}
Flag: 0ctf{c97155a5e288fa45f926b1058e4e0385d6ccde8513002885dc67948524bcbc85}