r0ops
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 上的函數:
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 個 byte,接著 呼叫 memcpy()
複製一些資料 最後把 rsp
修改為 0xE0AF8A0
後執行 ret
如同題目名稱, rsp
指向的資料區塊是個 rop chain... 透過肉眼辨識配合 ida pro 重新命名可以很快的把這些 function address 整理成對應的指令:
接者轉換成類 asm 格式:
pop rcx #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
( 中間省略 )
cmp rax, rbx
jne +rdx
pop rdx # 0FFFFFFFFFFFFFC38h
add rap, rdx
PrintFlag sub_DEAD3AF
不難發現 register 間的資料轉移都是夾在 add rsi, 8
與 sub rsi, 8
之間
再進一步簡化翻譯出 c code,其中 flag[8]
為我們送出的內容:
#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();
}
整理一下整個流程,簡單來說程式會從 port 13337 讀取 8 個 64bits 整數,計算出每個數的 13337 次方 mod 2 ^ 64 並與特定值比較,最後用這符合條件的 8 個數拼出 flag
在這裡可以發現程式組 flag 時其實是用 %08llx
做 format, 也就是說我們只需要知道目標數的最後 4 bytes,可以直接用暴力破解的方式爆出 crack.c:
#include <stdio.h>
void fuck(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 ) fuck(i);
}
最後手動組出 flag
Flag: 0ctf{c97155a5e288fa45f926b1058e4e0385d6ccde8513002885dc67948524bcbc85}