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