Rangkaian Digital
integer, long, float and double data type comparison bypass. complex stack pivoting.
Problem
Description
Selamat datang di pertemuan mata kuliah Rangkaian Digital! Materi hari ini adalah bilangan biner!
nc 103.181.183.216 17003
Hint 1: These leave instructions are so annoying man.... But I maybe able to use it to my advantage with that 2 byte overflow. Let me just teleport the stack around first to assemble my rop chain.
Hint 2: this should help bypass cool_thing2 and cool_thing3 https://baseconvert.com/ieee-754-floating-point
Hint 3: stack pivot ke akhir bss -> pake cool_thing2 buat nyusun ropchain -> pindahin rbp buat leak libc pake cool_thing3 -> pake got strstr buat gadget leave biar pivot ke bss lagi dan one_gadget deh ato ngga ret2syscall
Disclaimer
unsolved during the CTF, manage to solve after the event has ended
Proof of Concept
Analysis
given compiled binary and source code, we'll check its basic executable information and security perimeter. We're also given a Glibc version 2.35
Next, lets take a look at the given source code
The vulnerability is clearly a buffer overflow however the challenge presents us an obstacle that, namely two number a and b that needs to be same and different at the same time to be bypassed before we're able to do any ROP-ing. This proofed to be the hardest part since, during the event I was not able to bypass the checks on cool_thing2().
Cross Data Type Comparison
to bypass the checks on cool_thing1() is easy, the first check makes sure the numbers has to be different and is done using 64 bit registers. However the third comparison is done using the 32 bit register. Thus we can supply a number of which in its hexadecimal representation has the same value on its lower bytes but different on its higher bytes, one that I used is:
a = 0x7fffffffffffffff
b = 0x8fffffffffffffff
We can this in action in GDB
observe at both of our number stored on RDX and RAX, both compared to be not equal and passed the check
both of our number is again loaded onto RAX and RDX, since it's using 32 bit, it is going to ignore all of the other higher bits, thus when the check is made, both number are considered the same and passed the check.
for the checks on cool_thing2() to this day I was still unsure how it works and how to bypass it, I just used the numbers from the official writeup since I'm not too interested into it. For the checks on cool_thing3(), I was able to bypass it using random value on my first try and don't bother any further :p
Stack Pivoting
The existence of useless function though deemed useless at first, provide us the information of the existence of a large global variable called anu, which immediately set as our pivot target.
upon completing all the comparison, on cool_thing3() we're given the stack canary and 2 byte overflow, this meant we can only jump to hard coded addresses since they all are made of 0x40XXXX (with X as the value we control). Thus we need to pivot somewhere for a larger payload. Sadly this time there's no pop rsp;
gadget which would make this trivial, for this we need to take advantage of another gadget: leave; ret;
the leave instruction always exist at the end of every routine before returning. To properly understand this, we need to translate the instruction to something we far more familiar of:
leave -> mov rsp, rbp; pop rbp
ret -> pop rip
this means, on every buffer overflows, we practically have control over RBP thus RSP. Let's try to overwrite it with our pivot target.
on normal execution, the routine will return to 0x40134d which is just above 0x401404. This will be completely fine if we dont overwrite it but we will do the pivoting again and on the 2nd one we won't have this. So it's a good practice to standardize where you'll be returning after your exploit for a potential next stage.
we successfully controlled RBP, we just need another leave instruction to finally set our RSP to our destination. However let's check what the assembly code, what the program will do after it returns from cool_thing3() to cool_thing2()
this is equivalent to the read(0, buf, 64);
on the source code. Recall that since the stack is growing downwards, the program takes space relative to RBP minus some value of the size. This means we have to set our pivot not exactly on anu but at an offset to accommodate for the space the program wants to use so that we don't clobber with other data that is critical for the program execution
it's a great practice to take/put as much offset as possible since external calls will require large buffer size.
Next, we can pivot there nice... but without gadgets such as pop rdi and other trivial stuff, how can we craft our ropchain payload to leak Glibc addresses and achieve other stuff?
Observe the payload below
For a better understanding, Let's visualise the our "stack" after the payload is sent.
Next, at the of cool_thing2(), the program eventually will run leave; ret;
which exist at the end of every functions. After that call the stack will look like this
which again, because it it another leave; ret;
thus the program changes its stack and rsp again.
and thus the program continues its execution on 0x0401300. This whole crazy and complicated rop payload is just to accommodate for the missing and trivial pop rdi and pop rsp gadget which amazes me.
Non-Trivial Leaking Glibc
Now why are we jumping there and what's the GOT table is all about? Let's take a look at the assembly below
this code is located on cool_thing3(), which basically it prints whatever what ever on RBP - 0x8.
Inspecting the GOT table, if we were about to leak the runtime address for puts, then we need to supply the address for __stack_chk_fail.
Gaining Remote Code Execution
Next up since our execution continues at our convenience on cool_thing3() (where we started our pivoting), we start over our pivoting again and execute another ropchain. Since we have Glibc at our disposal we have the choice to utilise one_gadget or call system or even make a syscall. For convenience purposes, one_gadget is first tried and it works.
Flag
hacktoday{jU5t_tw05_c0MpL3m3Nt_4Nd_Fl0at1n9_p01nT_r3pREs3nt4t10n_pLU5_t3l3P0rT1ng_st4cK_fr4me_4R0uNd_VA_sp4C3}
Appendix
Last updated