Anti Flag
4 minutes to read
We have a binary called anti_flag
:
$ file anti_flag
anti_flag: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b8de97bc12c627606510140e43fc13e2efffcee5, for GNU/Linux 3.2.0, stripped
Decompilation
If we open it in Ghidra, we will see this main
function:
/* WARNING: Removing unreachable block (ram,0x00101525) */
undefined8 main(undefined8 param_1, undefined8 param_2, undefined8 param_3, undefined8 param_4, undefined8 param_5, undefined8 param_6) {
size_t sVar1;
long lVar2;
sVar1 = strlen(&DAT_00102011);
malloc(sVar1 << 2);
lVar2 = ptrace(PTRACE_TRACEME, 0, 1, 0, param_5, param_6, param_2);
if (lVar2 == -1) {
puts("Well done!!");
} else {
puts("No flag for you :(");
}
return 0;
}
Basically, it is using ptrace
to see if the program is being run within a debugger or not. So we have two different behaviors:
$ ./anti_flag
No flag for you :(
$ gdb -q anti_flag
Reading symbols from anti_flag...
(No debugging symbols found in anti_flag)
gef➤ run
Starting program: ./anti_flag
Well done!!
[Inferior 1 (process 2564048) exited normally]
However, the flag is not shown.
Assembly code analysis
In Ghidra, there is a warning. In fact, if we analyze the assembly code, we will notice that there is a block of unreachable code:
$ objdump -M intel -d anti_flag
anti_flag: file format elf64-x86-64
...
Disassembly of section .text:
00000000000010e0 <.text>:
...
1486: f3 0f 1e fa endbr64
148a: 55 push rbp
148b: 48 89 e5 mov rbp,rsp
148e: 48 83 ec 30 sub rsp,0x30
1492: 89 7d dc mov DWORD PTR [rbp-0x24],edi
1495: 48 89 75 d0 mov QWORD PTR [rbp-0x30],rsi
1499: c7 45 e4 00 00 00 00 mov DWORD PTR [rbp-0x1c],0x0
14a0: 48 8d 05 5d 0b 00 00 lea rax,[rip+0xb5d] # 2004 <ptrace@plt+0xf34>
14a7: 48 89 45 e8 mov QWORD PTR [rbp-0x18],rax
14ab: 48 8d 05 5f 0b 00 00 lea rax,[rip+0xb5f] # 2011 <ptrace@plt+0xf41>
14b2: 48 89 45 f0 mov QWORD PTR [rbp-0x10],rax
14b6: 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10]
14ba: 48 89 c7 mov rdi,rax
14bd: e8 de fb ff ff call 10a0 <strlen@plt>
14c2: 48 c1 e0 02 shl rax,0x2
14c6: 48 89 c7 mov rdi,rax
14c9: e8 f2 fb ff ff call 10c0 <malloc@plt>
14ce: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
14d2: b9 00 00 00 00 mov ecx,0x0
14d7: ba 01 00 00 00 mov edx,0x1
14dc: be 00 00 00 00 mov esi,0x0
14e1: bf 00 00 00 00 mov edi,0x0
14e6: b8 00 00 00 00 mov eax,0x0
14eb: e8 e0 fb ff ff call 10d0 <ptrace@plt>
14f0: 48 83 f8 ff cmp rax,0xffffffffffffffff
14f4: 75 13 jne 1509 <ptrace@plt+0x439>
14f6: 48 8d 3d 2e 0b 00 00 lea rdi,[rip+0xb2e] # 202b <ptrace@plt+0xf5b>
14fd: e8 8e fb ff ff call 1090 <puts@plt>
1502: b8 00 00 00 00 mov eax,0x0
1507: eb 44 jmp 154d <ptrace@plt+0x47d>
1509: 81 7d e4 39 05 00 00 cmp DWORD PTR [rbp-0x1c],0x539
1510: 74 13 je 1525 <ptrace@plt+0x455>
1512: 48 8d 3d 1e 0b 00 00 lea rdi,[rip+0xb1e] # 2037 <ptrace@plt+0xf67>
1519: e8 72 fb ff ff call 1090 <puts@plt>
151e: b8 00 00 00 00 mov eax,0x0
1523: eb 28 jmp 154d <ptrace@plt+0x47d>
1525: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
1529: 48 8b 4d f0 mov rcx,QWORD PTR [rbp-0x10]
152d: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
1531: 48 89 ce mov rsi,rcx
1534: 48 89 c7 mov rdi,rax
1537: e8 c3 fe ff ff call 13ff <ptrace@plt+0x32f>
153c: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
1540: 48 89 c7 mov rdi,rax
1543: e8 48 fb ff ff call 1090 <puts@plt>
1548: b8 00 00 00 00 mov eax,0x0
154d: c9 leave
154e: c3 ret
...
The ptrace
call is at offset 14eb
. After calling puts
, there is a jump to 154d
, which ends the program. However, there are instructions that are never executed between 1525
and 1548
.
We can also notice that Ghidra finds functions named FUN_001013ff
, FUN_001011c9
, FUN_00101201
and FUN_001012de
. Neither of those functions are called in the decompiled main
function, because of the inconditional jmp
instruction.
Patching
One way of solving this issue is by changing jmp 154d
(e8 28
in machine code) to nop; nop
(90 90
in machine code), so that there is no inconditional jump:
$ xxd -p anti_flag | tr -d \\n | sed s/eb28/9090/g | xxd -r -p > anti_flag_patched
Flag
Now, if we run the patched binary, we will see the flag:
$ chmod +x anti_flag_patched
$ ./anti_flag_patched
No flag for you :(
HTB{y0u_trac3_m3_g00d!!!}