<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>CTF on Kartone's Reversing Garage</title><link>https://blog.kartone.ninja/tags/ctf/</link><description>Recent content in CTF on Kartone's Reversing Garage</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 25 Mar 2019 09:00:00 +0000</lastBuildDate><atom:link href="https://blog.kartone.ninja/tags/ctf/index.xml" rel="self" type="application/rss+xml"/><item><title>An extensive step by step reverse engineering analysis of a Linux CTF binary</title><link>https://blog.kartone.ninja/when-a-reverse-me-ctf-binary-makes-you-loose-that-job/</link><pubDate>Mon, 25 Mar 2019 09:00:00 +0000</pubDate><guid>https://blog.kartone.ninja/when-a-reverse-me-ctf-binary-makes-you-loose-that-job/</guid><description>&lt;img src="https://blog.kartone.ninja/images/2019/03/sshot.png" alt="Featured image of post An extensive step by step reverse engineering analysis of a Linux CTF binary" /&gt;&lt;p&gt;&amp;hellip;or, in other words, &lt;em&gt;when failing to reverse a CTF binary makes you lose that job&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;During a past job interview, I was tasked to reverse four linux binaries of increasing difficulties as proof of my ability into the reverse engineering field. I solved the first two in a matter of an hour, the third one required me an entire day of work but sadly, I was not able to solve the last one. I don&amp;rsquo;t know if I wasn&amp;rsquo;t selected because of this fail, but it proved me one sure thing: I wasn&amp;rsquo;t prepared enough or, at least, as much as I wanted. Flash forward, I successfully ended up with another job, but that challenge kept staying there, like a small needle, in my head. During the following months, I studied and practiced a lot, mainly into firmware reversing field and, every now and then, I&amp;rsquo;ve tried to solve that sneaky challenge.&lt;/p&gt;
&lt;p&gt;This is my extensive and detailed description of my fails and success.&lt;/p&gt;
&lt;h4 id="important-note"&gt;Important note
&lt;/h4&gt;&lt;p&gt;Please note that as this analysis started some months ago and this post was reviewed a huge number of times, you won&amp;rsquo;t find same memory addresses or function names across the screenshots and code snippets.&lt;/p&gt;
&lt;h2 id="running-the-binary"&gt;Running the binary
&lt;/h2&gt;&lt;p&gt;With what are we dealing?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@kali:/opt/ctf# file original
original: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d0d5b9a34a4fe4c52a3939c75bd71cfa0dc23825, stripped
root@kali:/opt/ctf# checksec -f ./original
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No 0 2 ./original
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A standard, stripped, Linux 32bit binary with no fancy protection active. We&amp;rsquo;re not aiming to exploit it but only to find the flag. A picture is worth a thousand words, they say:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@kali:/opt/ctf# ./original
[-] No vm please ;)
root@kali:/opt/ctf# ./original AAAA
[-] No vm please ;)
root@kali:/opt/ctf# ./original -h
[-] No vm please ;)
root@kali:/opt/ctf# ./original AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[-] No vm please ;)
root@kali:/opt/ctf#
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It doesn&amp;rsquo;t run inside a virtual machine and I definitely don&amp;rsquo;t want to build a physical linux box. Would you tell me some of your internals, please?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@kali:/opt/ctf# strace ./original
execve(&amp;#34;./original&amp;#34;, [&amp;#34;./original&amp;#34;], 0x7fff2a7dc4f0 /* 48 vars */) = 0
strace: [ Process PID=121645 runs in 32 bit mode. ]
brk(NULL) = 0x572fb000
access(&amp;#34;/etc/ld.so.nohwcap&amp;#34;, F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7f05000
access(&amp;#34;/etc/ld.so.preload&amp;#34;, R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, &amp;#34;/etc/ld.so.cache&amp;#34;, O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=133840, ...}) = 0
mmap2(NULL, 133840, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf7ee4000
close(3) = 0
access(&amp;#34;/etc/ld.so.nohwcap&amp;#34;, F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, &amp;#34;/lib/i386-linux-gnu/libc.so.6&amp;#34;, O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
read(3, &amp;#34;\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\254\1\0004\0\0\0&amp;#34;..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1947056, ...}) = 0
mmap2(NULL, 1955712, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xf7d06000
mprotect(0xf7d1f000, 1830912, PROT_NONE) = 0
mmap2(0xf7d1f000, 1368064, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19000) = 0xf7d1f000
mmap2(0xf7e6d000, 458752, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x167000) = 0xf7e6d000
mmap2(0xf7ede000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d7000) = 0xf7ede000
mmap2(0xf7ee1000, 10112, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xf7ee1000
close(3) = 0
set_thread_area({entry_number=-1, base_addr=0xf7f060c0, limit=0x0fffff, seg_32bit=1, contents=0, read_exec_only=0, limit_in_pages=1, seg_not_present=0, useable=1}) = 0 (entry_number=12)
mprotect(0xf7ede000, 8192, PROT_READ) = 0
mprotect(0x565b8000, 4096, PROT_READ) = 0
mprotect(0xf7f34000, 4096, PROT_READ) = 0
munmap(0xf7ee4000, 133840) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xf7f06128) = 121646
waitpid(121646, [{WIFEXITED(s) &amp;amp;&amp;amp; WEXITSTATUS(s) == 1}], 0) = 121646
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=121646, si_uid=0, si_status=1, si_utime=0, si_stime=0} ---
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
brk(NULL) = 0x572fb000
brk(0x5731c000) = 0x5731c000
brk(0x5731d000) = 0x5731d000
write(1, &amp;#34;**[-] You fool, nobody debugs me!!**&amp;#34;..., 34[-] You fool, nobody debugs me!!!
) = 34
write(1, &amp;#34;1\n&amp;#34;, 21
) = 2
exit_group(-1) = ?
+++ exited with 255 +++
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;You fool, nobody debugs me!!!&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Great, after a few couples of runs, we know that there are some anti-VM and anti-debug code in place. Let&amp;rsquo;s look inside.&lt;/p&gt;
&lt;p&gt;First thing, I searched and found the strings pretty quickly, and I noticed also two other interesting strings: one for a fail, one for a success.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/Schermata-2019-03-12-alle-23.01.47.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Digging a little more, we can find where are placed the strings and from where they&amp;rsquo;re used for.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/Schermata-2019-03-12-alle-23.08.08.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s clear that the subroutine placed at address &lt;code&gt;0x566429DC&lt;/code&gt; has something to do with them and with the anti-VM/anti-debug tricks.&lt;/p&gt;
&lt;h2 id="analyzing-the-anti-debug-and-anti-vm-routine"&gt;Analyzing the anti-debug and anti-vm routine
&lt;/h2&gt;&lt;p&gt;Once I have identified where are the strings involved in this anti-debug and anti-vm tricks, it&amp;rsquo;s easy to find them and visualize the blocks in IDA. Please note that &lt;code&gt;sub_566429DC&lt;/code&gt; was here renamed in &lt;code&gt;AntiDebugAntiVM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/graph.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;This is the graph of the AntiDebugAntiVM functions. In the first block of code, we can see the standard function call convention that setup the stack frame. After that, a bunch of  &lt;code&gt;NOPS&lt;/code&gt; and a call to &lt;code&gt;fork()&lt;/code&gt;. Let&amp;rsquo;s understand the fork call, what&amp;rsquo;s its purpose?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;fork()&lt;/strong&gt; creates a new process by duplicating the calling process. The new process, referred to as the &lt;strong&gt;child&lt;/strong&gt;, is an exact duplicate of the calling process, referred to as the &lt;strong&gt;parent&lt;/strong&gt;. &lt;em&gt;On success, the PID of the child process is returned in the parent, and 0 is returned in the child&lt;/em&gt;. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately. (&lt;a class="link" href="http://man7.org/linux/man-pages/man2/fork.2.html" target="_blank" rel="noopener"
&gt;ref&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Basically, right after the fork call, its return value is saved into the &lt;code&gt;EAX&lt;/code&gt; register and then moved into a local variable that is compared with the zero value. The first branch is important: if the &lt;code&gt;JNZ&lt;/code&gt; is true, we&amp;rsquo;re into the parent process so we&amp;rsquo;re going into the right path. Vice versa, if the instruction is false we&amp;rsquo;re heading to the left or into the child process.&lt;/p&gt;
&lt;h3 id="into-the-child-process"&gt;Into the child process
&lt;/h3&gt;&lt;p&gt;If &lt;code&gt;EAX&lt;/code&gt; is zero, or in other terms, we&amp;rsquo;re into the child process, we can see a call to &lt;code&gt;getppid()&lt;/code&gt;function that &lt;a class="link" href="http://man7.org/linux/man-pages/man2/getpid.2.html" target="_blank" rel="noopener"
&gt;returns the process ID of the parent of the calling process&lt;/a&gt;.  But the important call is the next one, the call to the &lt;code&gt;ptrace()&lt;/code&gt; function. The standard definition of this function is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;ptrace&lt;/strong&gt;() system call provides a means by which one process (the&amp;quot;tracer&amp;quot;) may observe and control the execution of another process(the &amp;ldquo;tracee&amp;rdquo;), and examine and change the tracee&amp;rsquo;s memory and registers. It is primarily used to implement breakpoint debugging and system call tracing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And is defined as:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In assembly, the call is built with these lines of code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;push 0 ; *data
push 0 ; *addr
push [ebp+var_1C] ; Parent PID
push 10h ; _ptrace_request
call _ptrace
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Basically, the child retrieve its PPID and tries to &lt;em&gt;attach a debugger&lt;/em&gt; [1]&lt;em&gt;,&lt;/em&gt; if it fails, it&amp;rsquo;s the evidence that it is being debugged so sleep 5 seconds, detach and returns [2] . Otherwise returns anyway [3]. Going up a level, if the &lt;code&gt;fork()&lt;/code&gt; return &lt;code&gt;-1&lt;/code&gt; so returns with the status code &lt;code&gt;1&lt;/code&gt; [5]&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/Schermata-2019-03-14-alle-20.48.53.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h3 id="into-the-parent-process"&gt;Into the parent process
&lt;/h3&gt;&lt;p&gt;If &lt;code&gt;EAX&lt;/code&gt; is not zero, we&amp;rsquo;re in the right path, so in the parent process. As you can remember, we have the PID of the child into the &lt;code&gt;EAX&lt;/code&gt; register. After the check with &lt;code&gt;-1&lt;/code&gt; into the block [1], it goes into the block [2]. Here, the parent performs a call to &lt;code&gt;waitpid()&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;push 0 ; options
lea eax, [ebp+stat_loc]
push eax ; stat_loc
push [ebp+pid] ; child PID
call _waitpid
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;waitpid()&lt;/code&gt; system call is used to wait for state changes in a child of the calling process, and obtain information about the child whose state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a signal. In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is not performed, then the terminated child remains in a &amp;ldquo;zombie&amp;rdquo; state. (&lt;a class="link" href="http://man7.org/linux/man-pages/man2/wait.2.html" target="_blank" rel="noopener"
&gt;ref&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/Schermata-2019-03-14-alle-21.05.54.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On success, &lt;code&gt;waitpid()&lt;/code&gt; returns the process ID of the child whose state has changed; On error, &lt;code&gt;-1&lt;/code&gt; is returned. In the next blocks 2, 3, 4 and 5 what happens is described in this answer I got on &lt;a class="link" href="https://reverseengineering.stackexchange.com/questions/20808/fork-and-waitpid-calls-in-a-ctf-linux-binary/20818?noredirect=1#comment33616_20818" target="_blank" rel="noopener"
&gt;ReverseEngineering&lt;/a&gt;. There&amp;rsquo;s no need to add anything more.&lt;/p&gt;
&lt;h3 id="anti-vm-code"&gt;Anti-VM code
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/image-3.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;This is where things become fun and interesting. We can observe a bunch of &lt;code&gt;mov&lt;/code&gt; instructions into the stack, a loop and inside of it an interesting &lt;code&gt;xor&lt;/code&gt; instruction: &lt;code&gt;xor eax, 75h&lt;/code&gt;. It seems to be a loop that cycle &lt;code&gt;0x32&lt;/code&gt; times (&lt;code&gt;50&lt;/code&gt;in decimal) and starting from &lt;code&gt;[ebp+command]&lt;/code&gt; it &lt;em&gt;xors&lt;/em&gt; one byte at a time to a fixed value equal to &lt;code&gt;\x75&lt;/code&gt;. Pretty standard XOR decryption routine, right? We can try to replicate this routine in python:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/python
hexdata = &amp;#34;19061605005509551207100555523D0C051007031C061A07525509550107555811555255525509551600015558114F55581347&amp;#34;
binary = hexdata.decode(&amp;#34;hex&amp;#34;)
def xor_strings(data):
return &amp;#34;&amp;#34;.join(chr(ord(data[i]) ^ 0x75) for i in range(len(data)))
xored = xor_strings(binary)
print &amp;#34;Your decrypted string is: &amp;#34; + xored
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;root@kali:/opt/ctf# ./script.py
Your decrypted string is: lscpu | grep &amp;#39;Hypervisor&amp;#39; | tr -d &amp;#39; &amp;#39; | cut -d: -f2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Basically, it decrypts &lt;em&gt;in memory&lt;/em&gt; a shell command and execute it via the next &lt;code&gt;popen&lt;/code&gt; syscall that verifies, using the &lt;code&gt;lscpu&lt;/code&gt; command, if the CPU name contains a string &lt;code&gt;Hypervisor&lt;/code&gt;. This syscall looks pretty interesting:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The  &lt;code&gt;popen()&lt;/code&gt; function opens a process by creating a pipe, forking, and invoking the shell. Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only. The command argument is a pointer to a null-terminated string containing a shell command line.  This command is passed to /bin/sh using the -c flag; interpretation, if any, is performed by the shell. The type argument is a pointer to a null-terminated string which must contain either the letter &amp;lsquo;r&amp;rsquo; for reading or the letter &amp;lsquo;w&amp;rsquo; for writing. &lt;code&gt;popen()&lt;/code&gt;: on success, returns a pointer to an open stream that can be used to read or write to the pipe; if the &lt;a class="link" href="http://man7.org/linux/man-pages/man2/fork.2.html" target="_blank" rel="noopener"
&gt;fork(2)&lt;/a&gt; or &lt;a class="link" href="http://man7.org/linux/man-pages/man2/pipe.2.html" target="_blank" rel="noopener"
&gt;pipe(2)&lt;/a&gt; calls fail, or if the function cannot allocate memory, &lt;code&gt;NULL&lt;/code&gt; is returned.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After the stream is opened, another syscall &lt;code&gt;fgetc()&lt;/code&gt; is executed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;fgetc()&lt;/code&gt; reads the next character from &lt;em&gt;stream&lt;/em&gt; and returns it as an        &lt;em&gt;unsigned char&lt;/em&gt; cast to an &lt;em&gt;int&lt;/em&gt;, or EOF on end of file or error.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What happens is simple: it opens a stream, in read-only mode, and executes the command &lt;code&gt;'lscpu | grep 'Hypervisor' | tr -d ' ' | cut -d: -f2'&lt;/code&gt; . If it returns something, so the &lt;code&gt;grep&lt;/code&gt; returns something, we&amp;rsquo;re in a virtual machine, prints the string: &lt;code&gt;[-] No vm please ;)&lt;/code&gt; and exit. If the stream fails or it does not return anything, it closes the stream via the &lt;code&gt;fclose()&lt;/code&gt; syscall and returns.&lt;/p&gt;
&lt;p&gt;Everything becomes clear if we look now into the pseudo-code, with important variables renamed as their role.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;int AntiDebugAntiVM()
{
char command; // [esp+4h] [ebp-54h]
char v2; // [esp+5h] [ebp-53h]
char v3; // [esp+6h] [ebp-52h]
char v4; // [esp+7h] [ebp-51h]
char v5; // [esp+8h] [ebp-50h]
char v6; // [esp+9h] [ebp-4Fh]
char v7; // [esp+Ah] [ebp-4Eh]
char v8; // [esp+Bh] [ebp-4Dh]
char v9; // [esp+Ch] [ebp-4Ch]
char v10; // [esp+Dh] [ebp-4Bh]
char v11; // [esp+Eh] [ebp-4Ah]
char v12; // [esp+Fh] [ebp-49h]
char v13; // [esp+10h] [ebp-48h]
char v14; // [esp+11h] [ebp-47h]
char v15; // [esp+12h] [ebp-46h]
char v16; // [esp+13h] [ebp-45h]
char v17; // [esp+14h] [ebp-44h]
char v18; // [esp+15h] [ebp-43h]
char v19; // [esp+16h] [ebp-42h]
char v20; // [esp+17h] [ebp-41h]
char v21; // [esp+18h] [ebp-40h]
char v22; // [esp+19h] [ebp-3Fh]
char v23; // [esp+1Ah] [ebp-3Eh]
char v24; // [esp+1Bh] [ebp-3Dh]
char v25; // [esp+1Ch] [ebp-3Ch]
char v26; // [esp+1Dh] [ebp-3Bh]
char v27; // [esp+1Eh] [ebp-3Ah]
char v28; // [esp+1Fh] [ebp-39h]
char v29; // [esp+20h] [ebp-38h]
char v30; // [esp+21h] [ebp-37h]
char v31; // [esp+22h] [ebp-36h]
char v32; // [esp+23h] [ebp-35h]
char v33; // [esp+24h] [ebp-34h]
char v34; // [esp+25h] [ebp-33h]
char v35; // [esp+26h] [ebp-32h]
char v36; // [esp+27h] [ebp-31h]
char v37; // [esp+28h] [ebp-30h]
char v38; // [esp+29h] [ebp-2Fh]
char v39; // [esp+2Ah] [ebp-2Eh]
char v40; // [esp+2Bh] [ebp-2Dh]
char v41; // [esp+2Ch] [ebp-2Ch]
char v42; // [esp+2Dh] [ebp-2Bh]
char v43; // [esp+2Eh] [ebp-2Ah]
char v44; // [esp+2Fh] [ebp-29h]
char v45; // [esp+30h] [ebp-28h]
char v46; // [esp+31h] [ebp-27h]
char v47; // [esp+32h] [ebp-26h]
char v48; // [esp+33h] [ebp-25h]
char v49; // [esp+34h] [ebp-24h]
char v50; // [esp+35h] [ebp-23h]
char v51; // [esp+36h] [ebp-22h]
char v52; // [esp+37h] [ebp-21h]
int stat_loc; // [esp+38h] [ebp-20h]
__pid_t ParentPID; // [esp+3Ch] [ebp-1Ch]
FILE *stream; // [esp+40h] [ebp-18h]
__pid_t ChangedStateChildPID; // [esp+44h] [ebp-14h]
__pid_t ChildPID; // [esp+48h] [ebp-10h]
unsigned int i; // [esp+4Ch] [ebp-Ch]
ChildPID = fork();
if ( !ChildPID )
{
ParentPID = getppid();
if ( ptrace(PTRACE_ATTACH, ParentPID, 0, 0) )
{
stat_loc = 1;
exit(1);
}
sleep(5u);
ptrace(PTRACE_DETACH, ParentPID, 0, 0);
exit(0);
}
if ( ChildPID == -1 )
exit(1);
do
ChangedStateChildPID = waitpid(ChildPID, &amp;amp;stat_loc, 0);
while ( ChangedStateChildPID == -1 &amp;amp;&amp;amp; *__errno_location() == 4 );
if ( BYTE1(stat_loc) )
{
printf(&amp;#34;[-] You fool, nobody debugs me!!!\n%d\n&amp;#34;, BYTE1(stat_loc));
exit(-1);
}
command = 0x19;
v2 = 6;
v3 = 0x16;
v4 = 5;
v5 = 0;
v6 = 0x55;
v7 = 9;
v8 = 0x55;
v9 = 0x12;
v10 = 7;
v11 = 0x10;
v12 = 5;
v13 = 0x55;
v14 = 0x52;
v15 = 0x3D;
v16 = 0xC;
v17 = 5;
v18 = 0x10;
v19 = 7;
v20 = 3;
v21 = 0x1C;
v22 = 6;
v23 = 0x1A;
v24 = 7;
v25 = 0x52;
v26 = 0x55;
v27 = 9;
v28 = 0x55;
v29 = 1;
v30 = 7;
v31 = 0x55;
v32 = 0x58;
v33 = 0x11;
v34 = 0x55;
v35 = 0x52;
v36 = 0x55;
v37 = 0x52;
v38 = 0x55;
v39 = 9;
v40 = 0x55;
v41 = 0x16;
v42 = 0;
v43 = 1;
v44 = 0x55;
v45 = 0x58;
v46 = 0x11;
v47 = 0x4F;
v48 = 0x55;
v49 = 0x58;
v50 = 0x13;
v51 = 0x47;
v52 = 0;
for ( i = 0; i &amp;lt;= 50; ++i )
*(&amp;amp;command + i) ^= 0x75u;
stream = popen(&amp;amp;command, &amp;#34;r&amp;#34;);
if ( stream &amp;amp;&amp;amp; fgetc(stream) != -1 )
{
puts(&amp;#34;[-] No vm please ;)&amp;#34;);
exit(-1);
}
return fclose(stream);
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="first-round-of-conclusions"&gt;First round of conclusions
&lt;/h2&gt;&lt;p&gt;Right now it may seem pretty easy, but for me at that time, this was impossible to understand and represented the first big fail: I was not prepared with interpreting assembly XOR instruction, decryption loops and Linux syscalls. I spent almost an entire weekend on this and failed so hard. Because of the time constraints of the job selection, I sent my results without this last exercise and &lt;em&gt;maybe&lt;/em&gt; this influenced my performance into the selection. How to bypass all these checks? We need to find from where this function is called and maybe we could modify the code flow to avoid this calling.&lt;/p&gt;
&lt;h2 id="jumping-away"&gt;Jumping away
&lt;/h2&gt;&lt;p&gt;With the IDA basic functionalities, we can find where this function is called and, luckily for us, it&amp;rsquo;s called from a single location:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/image-4.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;The instruction that calls the function is located inside this &lt;code&gt;sub_E00&lt;/code&gt; and, in particular, IDA shows that&amp;rsquo;s the instruction: &lt;code&gt;call ds:(off_2EF0-3000h) [ebx+edi*4]&lt;/code&gt;. Looking around this code we can patch the &lt;code&gt;jz short loc_E55&lt;/code&gt; into a &lt;code&gt;jmp&lt;/code&gt;, so we would be able to circumvent all of the above protections.&lt;/p&gt;
&lt;h3 id="cheating-with-the-shell"&gt;Cheating with the shell
&lt;/h3&gt;&lt;p&gt;If you don&amp;rsquo;t want to patch the binary, there&amp;rsquo;s another way to fool this VM check, but &lt;strong&gt;not&lt;/strong&gt; the anti-debug. If you notice, the command passed as an argument to the &lt;code&gt;popen&lt;/code&gt; syscall is a normal shell command but with a &lt;em&gt;relative path&lt;/em&gt;. So quick and dirty trick would be to create a fake &lt;code&gt;lscpu&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/bin/bash
echo &amp;#34;I will run you anyway in this VM&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Be sure to export the directory inside the PATH variable and, basically, you&amp;rsquo;re done: when the binary will try to execute the &lt;code&gt;lscpu&lt;/code&gt; command, it will run the fake one, it won&amp;rsquo;t return anything containing &lt;em&gt;Hypervisor&lt;/em&gt; string, the &lt;code&gt;grep&lt;/code&gt; would return nothing and the &lt;code&gt;fgetc&lt;/code&gt; consequently will read nothing. Basically, all checks are positive. Easy as it seems.&lt;/p&gt;
&lt;h2 id="analyzing-the-self-decrypting-and-injecting-routine"&gt;Analyzing the self decrypting and injecting routine
&lt;/h2&gt;&lt;p&gt;We can take advantages of the debugging capabilities of IDA and playing with breakpoints. Single stepping into the program flow, after the above routines, we land into this interesting piece of code:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/image-5.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;I spent &lt;em&gt;a lot&lt;/em&gt; of days trying to understand this routine: but it was worth it because I learned &lt;strong&gt;a lot&lt;/strong&gt;: I learned about linux syscalls like &lt;code&gt;mprotect&lt;/code&gt;, &lt;code&gt;calloc&lt;/code&gt; and also &lt;code&gt;memcpy&lt;/code&gt;. I learned about how the code could auto-decrypt and auto-inject inside the binary itself. Moreover, how can be possible to change memory protections back and forth. Indeed, it was very helpful to look around this code, side by side, with its decompiled version:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;int sub_CB5()
{
char v0; // si
size_t v1; // eax
char s; // [esp+8h] [ebp-30h]
char v4; // [esp+9h] [ebp-2Fh]
char v5; // [esp+Ah] [ebp-2Eh]
char v6; // [esp+Bh] [ebp-2Dh]
char v7; // [esp+Ch] [ebp-2Ch]
char v8; // [esp+Dh] [ebp-2Bh]
char v9; // [esp+Eh] [ebp-2Ah]
char v10; // [esp+Fh] [ebp-29h]
char v11; // [esp+10h] [ebp-28h]
char v12; // [esp+11h] [ebp-27h]
char v13; // [esp+12h] [ebp-26h]
char v14; // [esp+13h] [ebp-25h]
char v15; // [esp+14h] [ebp-24h]
char v16; // [esp+15h] [ebp-23h]
char v17; // [esp+16h] [ebp-22h]
char v18; // [esp+17h] [ebp-21h]
char v19; // [esp+18h] [ebp-20h]
char v20; // [esp+19h] [ebp-1Fh]
char v21; // [esp+1Ah] [ebp-1Eh]
char v22; // [esp+1Bh] [ebp-1Dh]
void *src; // [esp+1Ch] [ebp-1Ch]
_BYTE *v24; // [esp+20h] [ebp-18h]
void *addr; // [esp+24h] [ebp-14h]
size_t n; // [esp+28h] [ebp-10h]
size_t i; // [esp+2Ch] [ebp-Ch]
n = 320;
addr = 0;
v24 = &amp;amp;unk_E78;
mprotect(0, (size_t)((char *)&amp;amp;unk_E78 - 0xFFFFD000 - 12288), 6);
s = 0xF9u;
v4 = 0xFCu;
v5 = 0xFFu;
v6 = 0xE6u;
v7 = 0xF5u;
v8 = 0xE0u;
v9 = 0xF1u;
v10 = 0xF3u;
v11 = 0xFBu;
v12 = 0xF9u;
v13 = 0xFEu;
v14 = 0xF7u;
v15 = 0xFDu;
v16 = 0xE9u;
v17 = 0xF3u;
v18 = 0xFFu;
v19 = 0xF4u;
v20 = 0xF5u;
v21 = 0;
src = calloc(0x141u, 1u);
for ( i = 0; i &amp;lt; n; ++i )
{
v22 = *((_BYTE *)sub_89B + i);
v0 = v22 ^ 0x90;
v1 = strlen(&amp;amp;s);
*((_BYTE *)src + i) = *(&amp;amp;s + i % v1) ^ v0;
}
memcpy(sub_89B, src, n);
return mprotect(addr, v24 - (_BYTE *)addr, 4);
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="tldr"&gt;TL;DR
&lt;/h3&gt;&lt;p&gt;Before we go deep into the details of the single blocks of code, giving a general overview of what its final purpose is, may help its comprehension. First thing, the code changes via &lt;code&gt;mprotect&lt;/code&gt; function the memory protections, adding the write permission, of a specific part of its &lt;code&gt;.text&lt;/code&gt; section. After that, it copies, into the stack, &lt;em&gt;some bytes&lt;/em&gt; that will be revealed as a key for an afterward decryption. Before entering into the main loop, it allocates an array of bytes into the heap via &lt;code&gt;calloc&lt;/code&gt;. Specifically, the length of the array is &lt;code&gt;0x140&lt;/code&gt; bytes; this value is saved into a local variable placed into the stack at &lt;code&gt;[ebp+n]&lt;/code&gt; offset. The main loop is somehow complicated because it xors byte per byte some of its code, placed at &lt;code&gt;sub_89B+i&lt;/code&gt; offset, with a fixed constant &lt;code&gt;0x90&lt;/code&gt; and after, it xors it again with the aforementioned key on the stack. After that, it overwrites the code placed at &lt;code&gt;sub_89B&lt;/code&gt; offset, with these new values via the &lt;code&gt;memcpy&lt;/code&gt; call and returns after changing again the memory protections of that code section back to &lt;code&gt;read-execute&lt;/code&gt;. Let&amp;rsquo;s break in line by line, considering only the useful ones.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/Schermata-2019-03-20-alle-21.43.19.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Here, it setups the length of the future array in the variable placed on the stack at &lt;code&gt;[ebp+n]&lt;/code&gt; with the size of &lt;code&gt;0x140&lt;/code&gt; or &lt;code&gt;320&lt;/code&gt; elements of &lt;code&gt;1&lt;/code&gt; byte. After that, it prepares the arguments of the next call to &lt;code&gt;mprotect&lt;/code&gt;, that will change the protection, enabling write permission, on the the address &lt;code&gt;0x5657D000&lt;/code&gt;. Looking up the stack:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/Schermata-2019-03-20-alle-21.50.35.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Having &lt;code&gt;ESP&lt;/code&gt; pointing at &lt;code&gt;0xFFC344F0&lt;/code&gt;, the calling convention dictate that the arguments of a function must be pushed into the stack in reverse order. The &lt;code&gt;mprotect&lt;/code&gt; call is defined as: &lt;code&gt;int mprotect(void *addr, size_t len, int prot);&lt;/code&gt; with&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prot&lt;/code&gt; = &lt;code&gt;6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;len&lt;/code&gt; = &lt;code&gt;0xE78&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*addr&lt;/code&gt; = &lt;code&gt;0x5657D000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words: change the permission of the memory area of &lt;code&gt;3704&lt;/code&gt; bytes starting from address &lt;code&gt;0x5657D000&lt;/code&gt;, granting the writability via the &lt;code&gt;PROT_WRITE&lt;/code&gt; constant. More info of this syscall &lt;a class="link" href="http://man7.org/linux/man-pages/man2/mprotect.2.html" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;. But what&amp;rsquo;s inside this address? We&amp;rsquo;re inside the ELF header, basically the start of the entire binary.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/Schermata-2019-03-20-alle-22.13.44.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Going further, we can see the moving into the stack of some bytes, a call to &lt;code&gt;calloc&lt;/code&gt; to allocate an array of &lt;code&gt;320+1&lt;/code&gt; null bytes into the heap and the setup of a loop counter variable, placed at &lt;code&gt;[ebp+var_C]&lt;/code&gt;, with the same size of the array. We&amp;rsquo;re setting up a loop that will scan, byte per byte, a specific area of the binary located at &lt;code&gt;0x5657D89B&lt;/code&gt; - that is a &lt;strong&gt;fixed&lt;/strong&gt; value - and xor every byte, first with &lt;code&gt;0x90&lt;/code&gt; and after with those bytes that were moved into the stack. For better understand this loop, I suggest to read the answer I got &lt;a class="link" href="https://reverseengineering.stackexchange.com/questions/20935/decryption-loops" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;. When this decryption loop ends, we have the decrypted code inside the heap, into the allocated array. Code can now be replaced with the decrypted one via the &lt;code&gt;memcpy&lt;/code&gt; syscall. Finally, write permission can now be disabled and the routine can finish and return.&lt;/p&gt;
&lt;h2 id="second-round-of-conclusions"&gt;Second round of conclusions
&lt;/h2&gt;&lt;p&gt;Many days and months passed staring at me failing so hard into the understanding of this routine. But the feeling was still the same: I wanted to have that &lt;em&gt;&amp;quot;[+] Good job! ;)&amp;quot;&lt;/em&gt; string and I&amp;rsquo;ve always had the &lt;em&gt;Try Harder&lt;/em&gt; approach. Understanding this loop wasn&amp;rsquo;t easy, not even close. I asked for help and, luckily, I got plenty. This is what I got: &lt;strong&gt;don&amp;rsquo;t be afraid to ask for help but don&amp;rsquo;t blindly ask for a solution&lt;/strong&gt;. Work on that, demonstrate that you studied that thing and failed; People, eventually, will get that and will help you.&lt;/p&gt;
&lt;h2 id="towards-the-victory"&gt;Towards the victory
&lt;/h2&gt;&lt;p&gt;After executing the decryption function we land into the code below. First it verifies that the user submitted a password of the length of exactly &lt;code&gt;0x27&lt;/code&gt;, that is a fixed value coming from this instruction: &lt;code&gt;mov eax, (dword_56561058 - 56561000h) [ebx]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/verify_length_password.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Only if the password is exactly &lt;code&gt;39&lt;/code&gt; characters, it moves on into the &lt;code&gt;DecryptedFunction&lt;/code&gt;, passing the user&amp;rsquo;s password as the argument. The previous experience helped a lot to understand this function and the pseudo code generated by IDA is pretty nice.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/dec.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;int __cdecl DecryptedFunction(int UserSubmittedPassword)
{
int result; // eax
char v2; // [esp+0h] [ebp-38h]
char v3; // [esp+1h] [ebp-37h]
char v4; // [esp+2h] [ebp-36h]
char v5; // [esp+3h] [ebp-35h]
char v6; // [esp+4h] [ebp-34h]
char v7; // [esp+5h] [ebp-33h]
char v8; // [esp+6h] [ebp-32h]
char v9; // [esp+7h] [ebp-31h]
char v10; // [esp+8h] [ebp-30h]
char v11; // [esp+9h] [ebp-2Fh]
char v12; // [esp+Ah] [ebp-2Eh]
char v13; // [esp+Bh] [ebp-2Dh]
char v14; // [esp+Ch] [ebp-2Ch]
char v15; // [esp+Dh] [ebp-2Bh]
char v16; // [esp+Eh] [ebp-2Ah]
char v17; // [esp+Fh] [ebp-29h]
char v18; // [esp+10h] [ebp-28h]
char v19; // [esp+11h] [ebp-27h]
char v20; // [esp+12h] [ebp-26h]
char v21; // [esp+13h] [ebp-25h]
char v22; // [esp+14h] [ebp-24h]
char v23; // [esp+15h] [ebp-23h]
char v24; // [esp+16h] [ebp-22h]
char v25; // [esp+17h] [ebp-21h]
char v26; // [esp+18h] [ebp-20h]
char v27; // [esp+19h] [ebp-1Fh]
char v28; // [esp+1Ah] [ebp-1Eh]
char v29; // [esp+1Bh] [ebp-1Dh]
char v30; // [esp+1Ch] [ebp-1Ch]
char v31; // [esp+1Dh] [ebp-1Bh]
char v32; // [esp+1Eh] [ebp-1Ah]
char v33; // [esp+1Fh] [ebp-19h]
char v34; // [esp+20h] [ebp-18h]
char v35; // [esp+21h] [ebp-17h]
char v36; // [esp+22h] [ebp-16h]
char v37; // [esp+23h] [ebp-15h]
char v38; // [esp+24h] [ebp-14h]
char v39; // [esp+25h] [ebp-13h]
char v40; // [esp+26h] [ebp-12h]
unsigned __int8 v41; // [esp+27h] [ebp-11h]
int counter; // [esp+28h] [ebp-10h]
int v43; // [esp+2Ch] [ebp-Ch]
v43 = 0;
v2 = 0x93u;
v3 = 0x5E;
v4 = 0xB0u;
v5 = 0xB8u;
v6 = 0xC5u;
v7 = 0xD7u;
v8 = 0xACu;
v9 = 0x23;
v10 = 0xC3u;
v11 = 0xF0u;
v12 = 6;
v13 = 0x72;
v14 = 0xF4u;
v15 = 0x74;
v16 = 0x93u;
v17 = 0x52;
v18 = 0x74;
v19 = 0x72;
v20 = 0x30;
v21 = 0xEDu;
v22 = 0x8Bu;
v23 = 0x3D;
v24 = 4;
v25 = 0x58;
v26 = 0xD8u;
v27 = 0xE5u;
v28 = 0xA2u;
v29 = 0xCFu;
v30 = 0x8Au;
v31 = 0xEDu;
v32 = 0x8Bu;
v33 = 0x5C;
v34 = 0x5E;
v35 = 0x61;
v36 = 0xDCu;
v37 = 0x31;
v38 = 0xCFu;
v39 = 0x91u;
v40 = 0x82u;
for ( counter = 0; counter &amp;lt; PasswordLength; ++counter )
{
v41 = *((_BYTE *)AntiAnalysisFunction + counter + 0xC7);
if ( (v41 ^ *(_BYTE *)(counter + UserSubmittedPassword)) != *(&amp;amp;v2 + counter) )
{
v43 = 1;
break;
}
}
if ( v43 )
result = puts(&amp;#34;[-] Nope!&amp;#34;);
else
result = puts(&amp;#34;[+] Good job! ;)&amp;#34;);
return result;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It scans the user&amp;rsquo;s password, character by character, xoring it with a string retrieved from the binary itself. If every character matches it goes on and continue in the loop, otherwise it breaks. In the end, if everything is correct, it prints the beloved success string. How can we retrieve the correct flag? If we dump the &lt;code&gt;39&lt;/code&gt; bytes from the binary, from the correct addresses, and xor them with the hardcoded string, we can take advantage of the xor bidirectional nature. Although you can find more details &lt;a class="link" href="https://en.wikipedia.org/wiki/Exclusive_or" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;, we&amp;rsquo;re basically telling this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;A xor B = C
A xor C = B
B xor C = A
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;My first approach was to &lt;em&gt;bruteforce&lt;/em&gt; the routine: if the string submitted is, eventually, &lt;code&gt;\x41\x41\x41\x41\x41\x41\x41...&lt;/code&gt; we can step by step into the code and go into the final &lt;code&gt;cmp&lt;/code&gt; instruction, retrieve the byte that it compares to and change the ZERO flag to force the loop to continue and not to stop. Otherwise we can dump the contents of the memory and xor with the hardcoded string, as result we get the flag that needs to be submitted to the binary.&lt;/p&gt;
&lt;p&gt;We know that we need to get &lt;code&gt;39&lt;/code&gt; bytes from address &lt;code&gt;*((_BYTE *)AntiAnalysisFunction + 0 + 0xC7)&lt;/code&gt; to &lt;code&gt;*((_BYTE *)AntiAnalysisFunction + 0x27 + 0xC7)&lt;/code&gt;. Or from  &lt;code&gt;(0x5662A9DC + 0 + 0xC7) = 0x5662AAA3&lt;/code&gt; to &lt;code&gt;0x5662AACA = (0x5662A9DC + 0x27 + 0xC7)&lt;/code&gt;. We can apply the xor operation with the known string and we&amp;rsquo;re able retrieve the flag, finally.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Hardcoded: 93 5E B0 B8 C5 D7 AC 23 C3 F0 06 72 F4 74 93 52 74 72 30 ED 8B 3D 04 58 D8 E5 A2 CF 8A ED 8B 5C 5E 61 DC 31 CF 91 82
Memory dump: E8 18 FC FF FF 83 C4 10 85 C0 74 11 C7 45 E0 01 00 00 00 83 EC 0C 6A 01 E8 90 FB FF FF 83 EC 0C 6A 05 E8 46 FB FF FF
Flag hex: 7B 46 4C 47 3A 54 68 33 46 30 72 63 33 31 73 53 74 72 30 6E 67 31 6E 59 30 75 59 30 75 6E 67 50 34 64 34 77 34 6E 7D
Flag ascii: { F L G : T h 3 F 0 r c 3 1 s S t r 0 n g 1 n Y 0 u Y 0 u n g P 4 d 4 w 4 n }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.kartone.ninja/images/2019/03/victory.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions
&lt;/h2&gt;&lt;p&gt;This was a long journey that required a lot of effort and countless sleepless nights. It was worth it? &lt;strong&gt;Every single minute, without any doubt&lt;/strong&gt;. I hope this post will help you in your studies and if you spot any errors or want to help me in my journey into the reverse engineering world please leave a comment, tweet or e-mail.&lt;/p&gt;
&lt;p&gt;As always, &lt;strong&gt;Try Harder&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>Vulnhub Homeless - Writeup</title><link>https://blog.kartone.ninja/writeup/</link><pubDate>Thu, 28 Jun 2018 10:27:23 +0000</pubDate><guid>https://blog.kartone.ninja/writeup/</guid><description>&lt;p&gt;This writeup covers the Vulnhub CTF machine named &lt;a class="link" href="https://www.vulnhub.com/entry/homeless-1,215/" target="_blank" rel="noopener"
&gt;Homeless&lt;/a&gt; by &lt;a class="link" href="http://l33thacker.com/" target="_blank" rel="noopener"
&gt;Min Ko Ko&lt;/a&gt;. Honestly this was a hard box and I had a hard time with some really nasty tricks but finally, I learned a lot. Seriously, a lot.&lt;/p&gt;
&lt;p&gt;After booting up victim box and kali, initial phase, as always, is discovering the box:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*TOwUTPllTCg5Ht205mFr6A.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Discovering box&lt;/p&gt;
&lt;p&gt;Box had from my DHCP server address &lt;strong&gt;172.16.10.127&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mapping some ports:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*JS-Qb8JkC8KaY-5c4fsqDQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Scanning for open ports&lt;/p&gt;
&lt;p&gt;Nothing too much interesting, standard HTTP port and SSH port. What seems interesting is the robots.txt that gives some clue about a special wordlist that eventually will be used in the next phases. But, trust me, we are very far from there right now. :-)&lt;/p&gt;
&lt;p&gt;So, time to open up our browser and give a look around. What caught my attention is that somewhere on the page is rendered our browser User-Agent header:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*VuCjmWFnKTsX14bfLojZqg.png"
loading="lazy"
&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*QzF423KoCBiSSZA9WrDY6g.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;So, instantly, what comes to mind is Shellshock! Sadly I spent two entire days poking around with every single point of injection trying to take advantage of this bug but nothing showed up. Literally nothing. :-\&lt;/p&gt;
&lt;p&gt;So I went brutal and I downloaded every single piece of this website and analyzed every single evidence. Very much time but, in the end, well spent:&lt;/p&gt;
&lt;p&gt;This small file, &lt;strong&gt;favicon.jpg&lt;/strong&gt;, it’s not the usual one.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*_2FxLbePvd5TRcG_xL-Dew.png"
loading="lazy"
&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*L1lNxLaM3vOvMcqXWNM3DQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Another big trick. What’s this image? What’s his purpose? Again I spent another day analyzing this image without any luck…after a while I tried to insert what is written in that caption “Cyberdog Sledding Portal” inside the User-Agent header and…&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*G9Jx9GXkTlIAjHrMRWoXiQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;So, this little bastard was expecting some password to open it up. Again, bastard!&lt;/p&gt;
&lt;p&gt;Back on track again, we need to go to another location: /myuploader_priv. Seems pretty easy uh? Upload a PHP reverse shell and we go in. Sadly, for the second time, no:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*zwxQlB7FJ-hUn709E-blEQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;I tried to upload every kind of files, of every size, tried changing every single header but nothing. Every file I tried to upload was always too large. I went manually and tried to upload files containing one, two, three characters and so I was able to get the max allowed file size that is 8 BYTES. Seriously? What the f**ck is supposed to mean? No way to upload PHP shells or reverse shells because, as far as I know, the smallest code execution snippet is this: &lt;?=`$_GET[1]`?&gt;. No way to fit in this ridiculously 8 bytes limit. So, how we can step forward? Simple, manually.&lt;/p&gt;
&lt;p&gt;So, I found that the only commands we can execute are limited to two characters long, so with &amp;lt;?=’ls’ inside a file named sploit.php we found:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*l29CgWr1UIbeXRmPGq9uVQ.png"
loading="lazy"
&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*mETweNedGYd0khHRmI_DrA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;That was hard.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*9WCU2xpce_57HFXipdyv7A.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;But nothing compared to this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*SWNngAiPFAIa_0Ugk7FrPQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Ok, I really need a hint :-|&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*Ar-I86_xxW-nmnKcca3iEQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;After checking this login form and, in particular, that piece of code I really wanted to die.&lt;/p&gt;
&lt;p&gt;How can I suppose to break this one?&lt;/p&gt;
&lt;p&gt;Another three days passed and I was asking for some help on every single social media I had. Thanks to &lt;a class="link" href="https://twitter.com/devl00p" target="_blank" rel="noopener"
&gt;this guy&lt;/a&gt; that pointed me in this right direction, I found &lt;a class="link" href="https://github.com/thereal1024/python-md5-collision" target="_blank" rel="noopener"
&gt;this&lt;/a&gt; useful python script that can generate md5 collisions.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*gmpuG38oK9_dE0EVA1X3KA.png"
loading="lazy"
&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*1H3BgTb1nEt57MN6h7KvpA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;But, yeah, it’s not that easy, right?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*-ufeGkYLaRnHxOqF8m7syg.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;They’re binary and we can’t send them directly to the HTTP form without encoding problems that, essentially, will break the md5 signature…&lt;/p&gt;
&lt;p&gt;Maybe we can try to encode them:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*UXGlMKFq69bne3VOp2eQgw.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;And send them to their good form:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*K1rny6MLK1pYOxBT9LqeXA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Please, kill me and give me flag…not now:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*9DWMcnn9NfUB0wvHIdrsFA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Basically we have a command execution form:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*t1SJNjRLN7KqnVoUpQLUyA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;So we can have a shell via netcat. Luckily we have a good version of it:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*-l_ES9ZomzyVPqf_wxNFjQ.png"
loading="lazy"
&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*d_JQNlqHH31xlz31XXONFA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;And from now on, it’s pretty easy. Standard usual enumerating stuff:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*ya3ITqshay-QBN2AzFL1SA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;We have a username and we have a good wordlist: Rockyou.txt so we can brute force it. Sadly this was long, very long. But finally we had a shot:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*7HzxQhCJlN1gXOtyhU8vQQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;We can now have a real shell with lowpriv access:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*a1owys1JQ3Lkf9yxpfr3QQ.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Found and fixed a cronjob and modified it to send back a python reverse shell with root privileges:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*Xp466GDjNKcLGOGdSpk7vQ.png"
loading="lazy"
&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*3rgdG6yqUnf1i4HJTypghA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;And finally:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1600/1*NASfTsLDgZGoR0a9vW1roA.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Finally a Victory&lt;/p&gt;
&lt;p&gt;Yeah. Try Harder.&lt;/p&gt;</description></item></channel></rss>