Google foobar - living peacefully within the sandbox (or not?)

After another CTF, I decided to start getting into pwn and reverse tasks. That's why I started solving pwnable.kr challenges. At one point I was looking up what the hell is "Use after free". Then, a wild message appeared:

GoogleFoobar-2

Yeah, sure! It appeared to be G's platform for challenging people, who are interested in coding, with some obscure, but cool tasks. If you want to know more, google "google foobar".

The workflow within the platform is the following: you get a task "Write a program that does X. You can use python/java and here is a sandbox for your code. Make a function answer(s) that takes the input using the first argument and returns the result. We will run your code against 10 tests and tell you the amount of passed tests".

Okay, the first task was solved within a couple of hours (~22h before the deadline), so at that point I started looking around, trying to escape the sandbox.

Note: I was using Python, so I have no idea how Java sandbox worked.

What I noted first was that you can't output any data. Looks like, 1st file descriptor had been redirected to /dev/null. Okay, will try to survive without direct print.

What about sending data to my host using some network libraries? Most likely that won't work due to to some firewalls. However, experience taught me not to underestimate peoples' errors. Unfortunately, urllib, socket, requests - all have been removed.

Let's move on. Try calling some standard unix binary. os.system, subprocess.popen, etc said that the method was not found. However, there are some more interesting functions, one of the them is os,getcwd(), which actually worked. Remember: we can't see any data on stdout. However, it seems that exceptions are raised, but only once, so after raising an exception, the program is stopped (no more tests are ran).

Now we shall try to use stderr to output the data we want. I tried to make an exception with my data as an error text, the code is as follows:

raise Exception("123")

The code returned something similar:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception

No luck here. At that point I was about to give up, when I asked my friend (WGH) what he thinks about such a sandbox. He quickly gave me a payload:

raise type('mycustomtext', (Exception,), {})

Go try that in your Python interpreter. In case you are too lazy, here is the outcome:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.mycustomtext

That string rose an exception named mycustomtext! That is the thing! So I quickly outputted os.getcwd() which appeared to be /tmp.

Current directory turned out to be boring, so what I did is I printed an input string from one of the tests. I quickly wrote the following 'solution' and ran the tests against it:

def answer(s):
	raise type(s, (Exception,), {})

And it said something like

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in answer
__main__.[1, 2, 3, 4]

Okay, now we see the first test within the task. What's next?

Note: I did not print any data, besides the current working directory and the first test from one of the tasks (which I did solve myself). That's why the following statements are theoretical, although I don't see a reason for them not to work.

Next, you could hardcode your solution in a way that the first test is passed (remember: you already have the input, all you need to do is solve the task for that input in any way). On the second test, the solution should output the input it just received (raising exception with a custom name). Repeat that procedure until you have all the tests.

-What is the purpose of this?

-I dont know :) That was just fun to bypass the restrictions.

-What else could have been done, printing the data?

-I don't know, probably, you could get solutions of other people or some other data within the sandbox.

I reported that bug, using Google foobar feedback form. However, I never received any answer, at least saying "thanks, fixed". Let's leave that up to them.

The bug was fixed within a couple of days, however, I managed to get a slow and obscure, but still, a valid bypass.

Will speak about that next time.

Cheers.