Binary world for binary people :)

samedi 20 mai 2017

Today we'll talk about abusing exit handlers in order to hijack the control flow.

This research stemmed from Google Project Zero article about heap overflow
NULL byte poisoning where they described using __exit_funcs or tls_dtor_list
to achieve code execution.
The issue I had was to find a way to resolve reliably these
non-exported symbols and access them.

The exit handlers are quite interesting as it is an easy version to do ROP
as they all take one parameter.
Functions such as setuid(), system() or other functions needing 1 parameter
can thus be easily called.

Pointer mangling is a mitigation implemented in order to thwart
direct function pointer corruption.
I'll show in this post how it can be bypassed.

We'll first analyze the code leading to the execution of these exit handlers
and then show how to trigger them.
There will be a lot of pasted listing ahead, these will be explained as we go.

Where is the code leading to executing these exit handlers?

About exit ()

Whenever we call libc exit(), it calls all the handlers we registered
with atexit() and on_exit() before calling the _exit() syscall.

We can see that "__run_exit_handlers()" does use pointer demangling by using
PTR_DEMANGLE() before dereferencing the function pointers and calling
the pointed code.
We will thus need to analyze how the mangling and demangling is done in order
to bypass it.

We first see that it tries to call "__call_tls_dtors()", this is interesting
as this called function is used to call destructors in tls_dtor_list,
we'll come back to it.

Each handler can have 5 flavors : ef_free, ef_us, ef_on, ef_at and ef_cxa.
Depending on the flavor of the exit handler, we'll have a function pointer,
argument and/or dso handle.
The function list can store at most 32 handlers and a linked list is created
if more is needed.
idx is the total number of functions and is 1-based (not 0-based as usually).

And our PTR_MANGLE() and PTR_DEMANGLE() definitions in "sysdeps/unix/sysv/linux/x86_64/sysdep.h".

About Thread Control Block

Like we saw in PTR_MANGLE() and PTR_DEMANGLE(), it all has to do with
the structure "tcbhead_t".
This structure is what's stored at FS, which correspond to the per thread data
(TCB probably for Thread Control Block).

So at fs:0x30 we get the pointer_guard.

It's the pointer guard as defined in "sysdeps/x86_64/nptl/tls.h" in the
structure "tcbhead_t".

We could go look the code at "_dl_setup_pointer_guard()" but research was not
done there.

We still need to determine where we can hit and overwrite these handlers.
Let's start with __exit_funcs.

About atexit() and finding __exit_funcs

The "atexit()" code is located in "cxa_atexit.c"

/* Register a function to be called by exit or when a shared library
is unloaded. This function is only called from code generated by
the C++ compiler. */
int
__cxa_atexit (void (*func) (void *), void *arg, void *d)
{
return __internal_atexit (func, arg, d, &__exit_funcs);
}
libc_hidden_def (__cxa_atexit)

What's interesting is "__exit_funcs" being used.
"__exit_funcs" is an un-exported function but we can resolve it by disassembling
that piece of assembly with capstone and retrieving the needed VA.
"__cxa_atexit()" is an exported symbol so we can retrieve the VA easily using
pwntools.elf.ELF.
You can see at VA 0x3a28a that it calculates the address of "__exit_funcs".

You can see at VA 0x3a5c6 that it dereferences the pointer to tls_dtor_list.
So we can disassemble that function and find that offset using capstone.
"__call_tls_dtors" is exported so the address can be easily parsed out
using pwntools.elf.ELF.

I didn't write code for it but the idea is the same as for __exit_funcs,
this is left as an exercise to the reader.

Bypassing pointer mangling

While playing with a binary challenge, I happened to see that _dl_fini()
is often registered in the __exit_funcs array, so we can recalculate
the pointer_guard value and thus bypass pointer mangling.

The issue with "_dl_fini()" is that it seems to be an un-exported symbol.
I've found the address while digging in gdb.
An elf parser probably has to be written to find "_dl_fini()" address.

A vulnerability that allows you to leak an encoded pointer in __exit_funcs
is also necessary.
Here we use _dl_fini encoded pointer.

The formula to compute the pointer_guard assuming that "_dl_fini()"
is used is as follow:

ptr_guard = ror (ptr_encoded, 0x11, 64) ^ _dl_fini

Here the code you've been waiting for. We re-use "get_exit_funcs()" that
was showed earlier.

Other (untested) ideas to get the pointer_guard?

There probably is another way to get that pointer_guard given you've got
an arbitrary infoleak. This may be possible through a pointer corruption
or a UAF or Type Confusion or something else.
If the attacker somehow manage to find where 'struct tcbhead' is located
in memory, he may be able to just read the value out of it.

Last idea is probably far fetched but let's look at it.
Let's say you got an oracle : crash or not crash and that your process
is respawned through a fork().
You could probably use techniques similar as those used for blind rop
to guess the pointer guard.
More research can be done there but we don't need it for now.

About glibc ptmalloc hooks

It may come a time where you somehow can't manage to exit a program running
as it may run in a infinite loop for example.

In order to use our previous technique, the process has to call
the libc exit() function.
This happens when the process prepare to exit.

We may be able to trigger that function before reaching the end of the program
by using glibc ptmalloc hooks.
In each glibc ptmalloc functions, there is a function pointer that is called
given it's not NULL.
By over-writing one of these hooks with glibc exit() function
and triggering the corresponding malloc(), free() or realloc() call,
we'll trigger the execution of our payload written in __exit_funcs.

These functions hook are all exported symbols that you can easily get with
pwntools.elf.ELF : __free_hook, __malloc_hook, __realloc_hook and __memalign_hook.

Conclusion

Full mitigations bypass is still possible nowadays on the latest
Linux distribution given the proper vulnerabilities and binary. Every technique
is applicable on a case-by-case basis.
Pointer mangling was implemented in order to make destructors corruption
exploitation harder, but as can be seen it's not impossible.

This technique is particularly useful when you don't know where the stack is
and you have full RELRO activated.
It allows you to do an easy version of ROP.

vendredi 31 mars 2017

I just took the OSCP Course and successfully passed the exam.
There are many other great reviews of the course out there, just thought I'd add my grain of salt.

I decided to take that course as I wanted to see where I was at in terms of hacking and penetration testing skills. As some of you know, I've been more or less playing with code, hacks and exploits there and there for some time now.
So yeah, it was time for something a little bit more formal and still hand-on.

About the PWK - OSCP lab

This training is an introductory course to penetration testing.
It is not an easy certification mainly due to the time that needs to be dedicated.

The lab is composed of a simulated company network, it is well thought out.
There are multiple sub-networks and dependencies between machines.

The goal is to hack and obtain administrator, SYSTEM or root privileges on as many machines as you can. You have no obligation to hack all the machines, but there are quite a lot of them which are interesting.

Machines run Linux, Windows or FreeBSD.
Vulnerabilities goes from 2000 to end of 2016.
You thus see a wide array of technologies, vulnerabilities and ways to hack into computers.

The course material consist of a PDF eBook and a couple of videos.
Apart from that, it's self-learning and sharing information on #offsec IRC or the forums. No information sharing about machines is tolerated though.

The staff is pretty helpful to confirm the track you're on without giving you anything that spoil the challenges.
They will help you and motivate you as long as you've shown that you worked hard and did the proper research.

Pre-Requisite

Many skills are recommended to have before-hand unless you want to really suffer:
- Linux and Windows command-line skills
- some basic scripting skills : some exploits modification are required in the lab
- training on vulnhub VMs will make your experience smoother and better
- IT experience : networking, development or security
You could start from 0, but as some people say : they've suffered even though they liked the course.
It is a HARD course for anyone starting in the field.
But it is necessary. No way around learning the hard way and suffering a little bit.

This is NOT an exploit development course, so you don't need to understand everything about assembly or buffer overflow or other exploitation techniques.
There are buffer overflows to exploit in the course, but these are explained sufficiently in details step by step in order for anyone taking the course to understand it.

My lab run

Pwning the lab

There are 30, 60 or 90 days packages, each including an exam attempt.

I took the 30 days package as I figured I'd extend by 30 days if necessary.
I ended up doing the whole lab in around 3 weeks and the remaining time for the lab report (150-300 pages depending on people).
Plan time for the lab report as access to the lab is necessary for some exercises.

I started my lab around the start of February and worked on it until start of March.

I was clocking in around 10-15h/day, yeah I had the opportunity to do the OSCP lab and exam full time so I did it.
In retrospect, it would have been better to take the 60 days package. It would have been a better balance.

The thing that took most of my time was recon, enumeration and post-exploitation.
Some machines can't be exploited frontally, there are dependencies between machines. Dependencies students have to find by themselves.

Taking notes while rooting boxes saved me a ton of time.
I was having around 2-5 boxes a day.
Fastest was 10 minutes, slowest was 5-6h.
While pwning boxes, I had enumeration tools running (dirbuster, etc).

How to do it in such a limited time frame?

- have time to dedicate to the course
- be organized : exploits, notes, TTP (Tactics Techniques Procedures), etc
- run multiple scans in parallel in a staged fashion (nmap top 1000 ports then full ports while you're analyzing the first scan, multiple dirbusters running targeting multiple machines, etc)
- statically compiled tools (if you've played with pivots, you probably know why ;))
- go after low hanging fruits first
- keep proper notes about every important steps you take
- do proper post-exploitation
- already have some experience
- Ask yourself the right questions. Don't blindly follow "exploitation guides", "penetration guides" or "privilege escalation guides". Ask yourselves what are the objectives and goal of each steps described?

How much time should I take?

This is really subjective.
I'll based those approximations from people I spoke to on IRC.
For someone with pentesting experience, been pwning quite some boxes, got time after work, I'd say 30-60 days.
For someone with a good development background, got some time after work, 3-6 months.
For someone with no IT skills, 1-3 years.

The most important thing being motivation and the time you can dedicate everyday to the course.
This is why this certification is hard : it will take your time, you need caring people and external distractions from time to time.

Use automated scripts or not?

I typed every single commands by hand, there are multiple reasons for that:
- faster : automated scripts are great but they run scans that can be useless in the end. For instance, if you managed to find a flaw resulting in Remote Code Execution in the found web app ... What use is there for running dirb, nikto, snmpwalk or SMB NULL enumeration?
Once access is gained, 'netstat' and file enumeration are faster and better "port scanners" and "version probers".
- automated scripts are not for speed, it is for consistency. Attack consistency can probably be fingerprinted and attack patterns be extracted.
- automated scripts are super noisy, rarely can fine tune the details
- better memorization
- better tool understanding (and thus better adaptation, not depending on a single tool)
- I can't allow a tool to interpret data for me without me being able to check that raw data. Having interpreted data AND raw data is really important, vulnerabilities can be in the details.

Other tips?

Getting good at hacking or pentesting is not all about the technical part.
Get breaks, go run, see friends, have beers, have a balanced life while doing the certification.
This certification sure is addictive, so be careful.

About the OSCP exam

The exam is hard, not for technical reasons but for the duration reason in my opinion. I'll come back to that later.
If you try the exam, it means you're kind of ready to validate your technical skills and knowledge.

The exam include 5 machines to hack.
Each machines are graded from 10 to 25 points.
10 points being the easiest and 25 points the hardest.You need 70 points to pass the certification.

It is subjective, depending on your skillset, the 25 points machines may be easy for you.

The duration reason : you got 23h45 minutes to validate the exam.
It looks like a long time, but hours do burn fast.

My exam run

A 0.5 box is an access obtained with low privileges.
A 1 box is an access obtained with full privileges.

The duration reason is the real hard part.
After some time, you also get tunnel vision and you get pretty tired. So take breaks.

Before the exam:
- a step back from hacking
- beers with friends
- going out a little bit
- some rest
It helps to disconnect from the subject to avoid tunnel vision.

So what I prepared:
- a super clean and tidy room and environment
- lots of food and drinks
- breaks and naps
- No coffee, red bull or whatever substances that people use in order to stay awake. It's a trap as you won't be able to have efficient naps. Naps were ultra super helpful.

I had 2 attempts.
In both attempts, I did not use any metasploit exploits, auxiliary or post module.

1st attempt

In retrospect I could have passed with the 1st try and here are my main mistakes:
- not following my intuition : "this smells vulnerable", almost at the end of the exam I ended up with a low privilege shell quickly on one of the box I was stucked on
- not rested enough before the exam
- too many tabs open in my browser : close them regularly

I failed for non technical reasons.
Don't take a fail as a failure but as a learning experience to further improve yourself for next time.

2nd attempt

Unfortunately, I got sick during the 48h preceding my scheduled exam time.
According to Offensive Security rules, no cancelation or re-scheduling is possible during that time. So I just went on with it.
I can say that it was really painful, between being tired, sick, coughing and the exam but I managed to get it.

After 4-5h + 1h (lunch) + 4-5h I had 1.5 boxes and was still poking around for recon and enumeration. I thought I was doomed.
I went to sleep for 4h, waked up, got some dinner and got 2.5 boxes in 5h. I had 4 boxes, enough to pass the exam.

Stucked on the 5th box, I tried to sleep for 2-3h, but ended up reading, playing games, googling, watching "The Flash" last episode, making sure my notes and screenshots were neat, small power nap.

Went back to the 5th box, couldn't gain access.
In the end, exhausted, I stopped around 3h before the end of the exam and went to sleep.

So during those 23h45, I got around 7h of sleep splitted
in 2, 15h of challenge time and 2h for entertainment and food. Refreshments during the whole exam. Those are
approximates.

The next day I wrote my report and got an hypothesis as how to root the last box. This hypothesis stemed from the feeling "IT IS vuln there" I had while attempting to root the 5th box.

The next day (today), got the email with the "pass" result, it was such a relief that I could go back to a normal life again.
I'll wait some time before passing OSCE, hopefully before the end of the year.

Conclusion

Everyone would say "try harder" but not everyone knows what it means.

Try harder is the embodiment of the following:
- keep at it
- be persistent
- recon, enumerate as much as you can
- research and research more
- ask yourself the proper questions and don't run tools blindly
- have a deep understanding of what's happening under the hood
"Try harder AND smarter."

It was a nerve-wrecking certification.
The most nerve-wrecking part was probably before submitting the final exam report. Spent a LOT of time checking and rechecking that nothing was missed.

Anyone can do it.
It is not as hard as everyone says it to be (impossible, etc).
It is not an easy certification though.
If you work hard, you are persistent and really keep at it, you'll end up getting it.
Don't be discouraged by people saying it's super hard or almost impossible to get. If you really really really want something and do something about it, then you'll get it.
You will end up having acquired hand-ons and practical skills actionable in the real world.
This is not some theoretical useless certification.

In all, it was an interesting and gratifying experience.
It was a really fun course if you like rooting boxes like I do.

mercredi 18 décembre 2013

So, last Saturday, I did a talk about Linux Kernel Exploitation.
I went over some well known vulnerabilities and I ended with a demo on a kernel exploitation challenge (here) by Jason Donenfeld (his site).The slides are at the end of this blog article.

In this post, I will detail a bit more some of the slides in the talk.I will not detail every single slides, only the ones where I think there isn't enough details. If you don't understand some things, don't hesitate to comment ;).

As you can see, uid, euid and suid will generally have the same value.
So if you set thos values to 0, your process basically has root privileges.
This heuristic is good enough as there is little chance that you will have 3 dwords with the same values in memory (don't forget we start to search from our current task_struct that represent our exploit process).

Root in 3 big steps

Prepare

This is the most important step as this will greatly affect the reliability of your exploit.

This is where you:
- check that the kernel is vulnerable.
- use information leaks
- prepare the memory layout so you can predict reliably where your objects are
- place your shell code in memory

The avantage of shellcoding in the kernel : it is in C.

Trigger vulnerability

This is where you will exploit your vulnerability.

Patching memory, pointers and whatsoever.

Trigger payload

This is where you escalate the privileges of your process.

This is also where you fix the mayhem you may have caused earlier.
It is REALLY important to fix the things you messed up as otherwise the machine may crash later.
It is done in the payload as the payload is executed in kernel mode. root is in userland, root != kernel land, don't get confused about that.

After triggering the payload, you go back in userland and spawn your root shell or whatsoever.

Ok, now that you have the basic understanding, you are ready for some kernel goodies.

Linux Kernel Exploitation

I won't explain CVE-2009-2692 unless some people ask for it.
It is simple enough using the slides to comprehend.

Anyhow, let's dig in TUN NULL Pointer Dereference.

TUN NULL Pointer Dereference

This vulnerability is really interesting as there is something really special about it : the vulnerability is NOT in the source code. It is inserted at compilation.
Basically, what happens is that tun is dereferenced before checking that tun is NULL. As such, GCC considers that the pointer doesn't need checking as we use it before checking : GCC removes the NULL check. Boom, vulnerability.

So the NULL check doesn't exist and tun is NULL.
So we can map the NULL page and we thus control tun->sk.
We control sk->sk_socket->flags as well.
test_and_set_bit() set the last bit at 1.
Bang, we can set any NULL pointer to 1.
In the exploit, mmap() is chosen as the TUN device doesn't have a mmap().
mmap() need to be see to one even though we control the NULL page as internally mmap() is not called if it's NULL.
Put a trampoline at address 1 to jump over all the junk you've set up and go to your payload.
And that it's, you've escalated your privileges.

So here it is, if mmap() is NULL, it doesn't get called.
That is why it sets the mmap() pointer to 1.

Other exploits

This is where it gets pretty hard to explain as there is still tons of code to read x).
I dug a bit in vmsplice, RDS and perf_events exploits.

vmsplice use buffer overflow, but it's not a common one as it doesn't overwrite any function or return pointers. What it overwrites are compound page addresses (values we don't control) and then call a dtor pointer the attacker control.
Privileged code execution is gained in put_compound_page() through the call of a destructor function pointer that we control.
This dtor pointer obviously points to the attacker payload.
At the end of the article, I've attached some analysis I did for vmsplice. There is lot of code to cover though so I won't detail it in this post.

I haven't thoroughly analyzed the RDS exploit yet but it is a write-what-where.

The perf_events exploit is really interesting.
It 'basically' increment a INT handler pointer upper bytes in 64 bits so the pointer end up in userland. The exploit then return to this allocated memory containing the payload.
The exploit also use a neat trick to compute the perf_event array. An entire post is necessary as well to properly understand this exploit. Analysis have already been done anyhow by other people.

The challenge

The VM is a 64 Bit Linux system made especially by Jason Donenfeld (aka zx2c4).
The vulnerability allows us to write a 0 anywhere in kernel memory.
As such, in my exploit, I zeroed out some part of a proto_ops function pointer. mmap() it, put my payload over there, jump to it and fix it.

I debugged the exploit using registry information showed when the exploit crashed.

The exploit is included in the archive below.

Conclusion

As you can see, kernel exploitation has some similitudes with userland exploitation.
The differences mainly stem in the protections and the impact that a bug can have.
For instance, in kernel-land, not initializing a structure fully can have severe consequence (code execution through NULL pointer dereference, etc) while in userland, it may cause an infoleak but not directly code execution.

Moreover, this also shows that the kernel is piece of software and is as such exploitable.

vendredi 13 décembre 2013

Today, I played a bit with Metasploitable 2.
It is really easy to root, so that's not the interest of this blog post.

Anyhow, I played a bit around and I ended up coding a basic LFI exploit tool.

So yet another post on LFI exploitation ...

So what is LFI?

LFI stands for Local File Inclusion.
It is a vulnerability that allows you to include local files.
Many people do think that it's not really dangerous as it only includes LOCAL files.
Unfortunately (depending on which side of the barrier you are ...), it is false, you can execute code through a LFI.

Apache logs

These were publicly accessible in old distros.
Now, these are only readable by proper users.

You'd basically inject PHP Code through the GET requests:

http://victim/<?php system ('id'); ?>

This would leave PHP code in the logs.

Then executing the PHP code is as simple as:

http://victim/?page=/var/log/apache2/access_log

Code execution if there is no proper rights on the logs (some old systems remain).

/proc/self/environ

This file is interesting as it stores stuffs like your USER-AGENT and whatsoever.

So, if you change your User-Agent to

<?php system ('id'); ?>

and use this:

http://victim/?page=/proc/self/environ

Yes, code execution!

php://input

Ok, this one execute PHP Code included into the POST DATA.

NULL byte injection and path truncation

This one is pretty neat.
Say you have the following code:

<?php include ($_GET['page'] . '.php'); ?>

Well, you can get rid of the '.php' extension using that trick.
Just append or looooooots of . or /., this will get normalized and voila no more extension.
NULL Byte poisoning doesn't work for PHP >= 5.3.4 as it's been fixed.

Reverse path truncation is mostly the same, just the ../ is before the file name.

PHP filters

This vulnerability is mainly for leaking files (.php and others).
This doesn't work if you have a prefix such as here:

image with PHP code

This one is about appending PHP code in an image.
Using the image in the LFI allows you to inject PHP code : the PHP interpreter interprets anything as code as long as it's in <?php ?>.

If you have a non exploitable LFI with /proc/self/environ or apaches logs and you don't have an extension concatenation, this can allow you to exploit it if you are able to upload images.
Let's say you have PHPBB and PhpLdapAdmin 1.1.0.5.
Well, you can upload an image using PHPBB then exploit the LFI in PhpLdapAdmin using the directory traversal trick => code execution.

Exploit

I wrote a basic LFI exploiter that uses PHP filter or /proc/self/environ tricks.
You can get it at LFI exploit tool .
The code isn't clean and it needs tons of improvement before being really a usable tool. I plan on improving it on a need to basis. The cookie functionality is not implemented yet, it is just a placeholder for now.
You can test it on multilidae on Metasploitable 2. I haven't tested it somewhere else yet.

Don't forget -0x8 which correspond to seip and sebp ;).
sbuffer is a temporary buffer which is used by fgets().
value1 and value2 are integers converted from sbuffer through atoll().
fp is the file pointer used for referencing the file.

The interesting code is between 0x804855a and 0x8048563, the rest is mostly
setting up stuffs.
From this code we can infer that:
- buffer is in fact an array of integers (due to shl eax, 0x2 which is equal to x4)
- value2 is an offset
- value1 is a value to insert
- no return as eax value is not properly re-initialized

So we can basically write an arbitrary value anywhere we want: a so called
"write-what-where".
This basically allow us to easily bypass the stack cookie.

Time for exploitation.

Exploiting a write-what-where

We first need to know what to write, there are multiple possibilities:
- SEIP (return to code)
- SEBP (then craft a fake stack frame and all necessary stuffs)
- atexit destructors
- DYNAMIC FINI
- GOT entry
- etc
Since this is a write-what-where and we want a reliable exploit, the
__stack_chk_fail GOT entry would have been a nice target.
Unlucky for us: RELRO so no way.
We will target a stack address as the buffer address is on the stack and we
control an offset. It will be more reliable than targeting an address in the
binary (stack randomization could make the offset change between 0x804.... and
stack addresses). SEIP is the obvious candidate here.
Since we would like to trigger our payload as soon as possible, insert() SEIP
is the target.

Second condition: we need a place for our shellcode!
We'll "simply" inject it through our "integers array". There is plenty of room
(0x1000 = 4096 bytes!).
Secondly, good news: no NX, so no need for ROP here.
Given that our shellcode is around 22-50 bytes, we get at least 4000 NOPs for
our NOPsled.

Ok, we got 0x3c (60) bytes upper on the stack and lower in memory.
So we basically are going to create an underflow.
Since there seems to be only unsigned integers but multiplied by 0x4. We need
to have "real" value:
3c = 15 * 4
Ok we got our offset, what about our value to insert?

We can avoid guessing the 4 bytes of the address.
We can do that by overwriting only 2 last bytes of insert() SEIP (it would
thus junk 2 bytes afterward but we don't really care about those).
Since we multiply by 4, this technique is not possible (it has to be a
multiple of 4).

Whoops, the first 4 bytes are never written.
That's explained by the fact that insert() isn't called if atoll() return 0.
We can fix it through 2 methods:
- put our payload somewhere else in the stack
- use arithmetic tricks

Putting our payload somewhere else

Remember about 0xb3d36f04 ?
This is our env[] pointer.

We need an address in stack to use the same trick as before with the pop-pop-ret.

The interesting line is here:
0x0804855a <+38>: shl eax,0x2 ; value2 <<= 2;
Our offset is multiplied by 4.
We can thus manage to overflow our offset so it becomes 0.
We want 0x1 0000 0000 so offset will be equal to 0x4000 0000.
That's how we get the zero!

Conclusion

As you can see, with a bit of work, motivation and some vulnerability, you can
bypass protections.
ASLR was of no use here as we bypass it through offsets and pointers laying on
the stack.
RELRO didn't stop us as we can write on the stack with a write-what-where.
If NX were to be set, it wouldn't have stopped us either as we can still craft a
rop chain. The problem would more have to been about the number of available
gadgets.