Everything can be API!

Posted by believer on Wed, 05 Jan 2022 11:42:42 +0100

This is the 544th technical sharing of "attacking Coder"

Author: Cui Qingcai

Today, I saw an open source project called Command2API. It's very interesting to share with you.

origin

The original Repo has a paragraph about why this project was born:

"Take the recent RCE of Log4j as an example. In the intranet security test, due to the limitation of the network environment, there is no DNSLog platform available. At this time, for Log4j vulnerability verification, we consider directly checking whether the LDAP service is connected. However, the ready-made JNDI injection tool opens the service and does not have an API to directly pull the results of the corresponding service, which requires manual checking Look, it takes a lot of time. In addition, the BurpSuite passive plug-in has been written for scanning. In order to save time, this script is simply written to obtain the execution results of JNDI tools and return them in the form of API, which is convenient for the plug-in to pull the results for vulnerability verification.

Anyway, the main idea is that if the execution results of some commands can be exposed through the HTTP API, we can more easily obtain the execution results of commands, which will be very convenient in some scenarios.

So, here the author wrote this project.

principle

This principle is actually very simple, that is, a Python thread is used to start the Web service, a thread executes the command, and the result of executing the command is shared with the Web service through global variables.

function

Here we run to see the effect.

First, you need to download the following items:

git clone https://github.com/gh0stkey/Command2API.git

Then specify the command you want to run and the port on which the API runs. The example is as follows:

python Command2Api.py "Commands executed" Web Running port

"Note that python here uses python 2 instead of python 3, because the original project references a package called BaseHTTPServer, but python 3 does not.

Here we execute a ping command to try:

python Command2Api.py "ping www.baidu.com" 8888 

The operation results are as follows:

As you can see, a running address is output here first:

URL: http://HOST:8888/c1IvlLF9

This is when we open http://localhost:8888/c1IvlLF9 Look.

You can see that the console results are displayed in the web page.

"But this page cannot be refreshed automatically. You need to click refresh to get the latest results.

The introduction is over.

Therefore, this project is very useful in some cases.

for instance:

  • In intranet security testing, it can be used to obtain the execution results of JNDI tools and return them in the form of API, so it is more convenient to observe the execution results.
  • We want to monitor or obtain the output results of a command-line program in real time, such as Scrapy crawler, Web Server, etc., which can be exposed.
  • If we want to quickly share the execution results of a program, we can use this command to generate a website with Ngrok to share it.

wait.

Source code analysis

Let's take a look at the source code. In fact, it is very simple. There are only these codes:

import subprocess
import BaseHTTPServer
import SimpleHTTPServer
import cgi
import threading
import sys
import string
import random

l = []

uri = '/' + ''.join(random.sample(string.ascii_letters+string.digits,8))

class thread(threading.Thread):
  def __init__(self, threadname, command):
    threading.Thread.__init__(self, name='Thread_' + threadname)
    self.threadname = int(threadname)
    self.command = command

  def run(self):
    global l
    ret = subprocess.Popen(
      self.command,
      shell=True,
      stdin=subprocess.PIPE,
      stdout=subprocess.PIPE,
      stderr=subprocess.PIPE
    )
    for i in iter(ret.stdout.readline, b""):
      res = i.decode().strip()
      print(res)
      l.append(res)

class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  def do_GET(self):
    global l
    if self.path == uri:
      self.send_response(200)
      self.send_header('Content-Type', 'text/plain')
      self.end_headers()
      self.wfile.write(l)

if __name__ == '__main__':
  # New Thread: Get Command Result
  t1 = thread('1', sys.argv[1])
  t1.start()
  # Webserver
  port = int(sys.argv[2])
  print("URL: http://HOST:{0}{1}".format(port, uri))
  Handler = ServerHandler
  httpd = BaseHTTPServer.HTTPServer(('0.0.0.0', port), Handler)
  httpd.serve_forever()

You can see that Popen executes this command, and then captures the result through PIPE and assigns it as a variable. At the same time, another thread starts the server and writes the result to the Response.

It is such a simple code that realizes such convenient functions.

optimization

However, I think there is still a lot of room for optimization in this project. To sum up briefly:

  • Python 2 is now supported instead of Python 3.
  • Web page results cannot be refreshed automatically.
  • The web page result is a list, which is not consistent with the result format of the console.
  • You cannot secure this toolkit through pip.
  • The HOST of the output results can be optimized and copied directly, which is difficult to access.
  • The results can be publicly exposed in cooperation with Ngrok.
  • It would be better if you could interactively control commands through web pages.

Let me see. If I have time, I can try to rewrite this project and realize some of the above optimization functions. Ha, I'll send it out when I'm finished.

Thank you for reading ~

End