Category Archives: vulnerabilities

Spotify Device Name and Folder Name XSS

Found some “Self-XSS” bugs on Spotify. This issue is caused by the mobile device name not being sanitized before being displayed by the WebView in the Spotify Desktop application as shown by the broken image tag. Unfortunately this bug did not qualify for bounty of any sort on Hackerone as it is considered a harmless “Self-XSS”.

The other folder name bug seems to be a duplicate that has been reported by someone else but not fixed. These bugs are considered harmless to Spotify and thus they have not been fixed.


Fuzzing with AFL: mpg123

CVE-2016-1000247: Denial of service with crafted id3v2 tags in all mpg123 versions since 0.60.


In the recent weeks, I was reading on the topic of fuzzing binaries and started experimenting as well as trying it out to find new interesting bugs. I decided to use the most user-friendly fuzzer, which is the American Fuzzy Lop (AFL). And thus, the AFL fuzzing journey began.

A close friend of mine recommended fuzzing some open source binaries as an exercise, and we chose to start with mpg123, a command line mp3 player. We managed to find a simple integer underflow denial of service bug, which happens when mpg123 attempts to parse a malformed ID3v2 tag size header. The bug report that was submitted is shown below.

Summary: Segfault (unsigned long underflow) in mpg123 when parsing malformed ID3 header size in id3.c:755:765.
Discovered by : Han Lee & Jerold Hoong

- beep.mp3 id3 header  : 4944 3303 0000 0000 0026 
- crash.mp3 id3 header : 4944 3303 0040 0000 0006

RUN with legit beep.mp3 file

Breakpoint 1, INT123_parse_new_id3 (fr=fr@entry=0x7421b0, first4bytes=<optimized out>) at src/libmpg123/id3.c:755
755                     while(tagpos < length-10) /* I want to read at least a full header */
(gdb) print tagpos
$1 = 0
(gdb) print length
$2 = 38
(gdb) print length-10
$3 = 28

RUN with malformed crash.mp3

Breakpoint 1, INT123_parse_new_id3 (fr=fr@entry=0x7421c0, first4bytes=<optimized out>) at src/libmpg123/id3.c:755
755                     while(tagpos < length-10) /* I want to read at least a full header */
(gdb) print tagpos
$1 = 1414546737
(gdb) print length
$2 = 6
(gdb) print length-10
$3 = 18446744073709551612
(gdb) n
Breakpoint 2, INT123_parse_new_id3 (fr=fr@entry=0x7421b0, first4bytes=<optimized out>) at src/libmpg123/id3.c:765
765                         if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
(gdb) n
759                         int head_part = fr->id3v2.version == 2 ? 3 : 4; /* bytes of frame title and of framesize value */
(gdb) n
765                         if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
INT123_parse_new_id3 (fr=fr@entry=0x7421b0, first4bytes=<optimized out>) at src/libmpg123/id3.c:765
765                         if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
(gdb) bt
#0  INT123_parse_new_id3 (fr=fr@entry=0x7421b0, first4bytes=<optimized out>) at src/libmpg123/id3.c:765
#1  0x0000000000448ac3 in handle_id3v2 (newhead=<optimized out>, fr=0x7421b0) at src/libmpg123/parse.c:1071
#2  wetwork (newheadp=<optimized out>, fr=<optimized out>) at src/libmpg123/parse.c:1241
#3  INT123_read_frame (fr=fr@entry=0x7421b0) at src/libmpg123/parse.c:536
#4  0x0000000000493862 in get_next_frame (mh=0x7421b0) at src/libmpg123/libmpg123.c:623
#5  mpg123_decode_frame (mh=0x7421b0, num=num@entry=0x741968 <framenum>, audio=audio@entry=0x7fffffffe2e0, bytes=bytes@entry=0x7fffffffe2e8) at src/libmpg123/libmpg123.c:859
#6  0x000000000042ecc8 in play_frame () at src/mpg123.c:763
#7  0x0000000000406735 in main (sys_argc=<optimized out>, sys_argv=<optimized out>) at src/mpg123.c:1369


Description: Access violation on source operand
Short description: SourceAv (19/22)
Hash: 6bd52e79e6017b9e236d4d9edea21a67.d278ec4c10e616aa909c08fc399072bd
Exploitability Classification: UNKNOWN
Explanation: The target crashed on an access violation at an address matching the source operand of the current instruction. This likely indicates a read access violation.
Other tags: AccessViolation (21/22)

Kudos to the upstream maintainer of mpg123, Thomas Orgis, who responded very quickly and released a fix for the bug as well as a perl one-liner hotfix for older versions. The source is available for download at, and debian has updated the mpg123 packages as well (


Manual Exploitation of Blind HQL Injections

Almost everyone has heard about SQL injections, but what about HQL injections? Hibernate Query Language (HQL) is an object-oriented query language, similar to Structured Query Language (SQL), but instead of operating on tables and columns, HQL works with persistent objects and their properties.

Imagine that you are doing a source-code review on a Java-based web application that uses Direct Web Remoting (DWR) and you come across the following line of code:

String statement = "select x from Rights x, Roles y where x.uId = y.uId " + "and y.roleId = " + rId;
List roles = DataUtils.getHQL().queryList(statement);

The ‘rId’ variable in the statement is taken directly from user input, thus making this line of code vulnerable. After tracing the ‘rId’ variable, it was found that the function that calls the vulnerable statement is also conveniently exposed via DWR. If you have not heard about DWR, it is a library that allows Java methods / functions to be exposed and called directly using JavaScript. (more info available here:

Using the JavaScript console in a web browser, it was possible to trigger the function call using the following command:


Note: The function names depends on how the developer of the web application implements the DWR library. One way to determine this is to observe the traffic through a proxy and look for the main DWR call that pulls the exposed functions’ JavaScript .js file. Take the JavaScript content of the file and manually load it in the browser console, so that you will be able to call the functions.

Calling the following exposed DWR call via JavaScript with the following parameter returns a response of Content-Length: 120:

DWRAction.getUserRightsByRoleID("1 or 1=2");

While calling the following exposed DWR call via JavaScript with the following parameter returns a response of Content-Length: 2649040:

DWRAction.getUserRightsByRoleID("1 or 1=1");

This looks like a confirmed case of a blind HQL injection vulnerability. The goal now would be to dump some important information from the database. Attempts were made to use SQLmap as well as an obscure HQLmap tool from GitHub, but they were not successful. Attempts were then made to exploit this vulnerability manually.

While playing with the injection parameters, it was observed that UNION queries were not allowed. Also, <, >, !, = comparators are filtered by the XSS filters. Single and double quotes were also filtered. You might ask why is the “1 or 1=1” input allowed with quotes? The reason behind this is because the exposed JavaScript DWR method takes in a String, therefore it has be sent to the server enclosed in quotes [DWRAction.getUserRightsByRoleID(String);].

Consulting the official HQL manual shows that the following expressions are supported:


After a bit of trial and error with the supported expressions, it was observed that the accepted inputs are:

  • ( ) Parentheses
  • Between x and y (this will be the alternative to < > comparators)
  • Sub SELECT statements allowed (alternative to UNION)

With this knowledge, the next step would be to find out valid tables and columns. Searching through the source code for other HQL statements gave some idea of what tables and columns exists. In this case, the table name was ‘TBL_USERS’ with several columns, but what we are interested in is just the “password” and “uId” columns.

The first step in order to extract some meaningful data will require a valid ‘uId’ from the ‘TBL_USERS’ table. The following sub SELECT payload was used:

DWRAction.getUserRightsByRoleID("1 or (select id from TBL_USERS where id between 1 and 2000) = 1");

After tweaking the ‘between’ values manually by elimination and looking at the server responses, it was observed that the ‘uId’ of value 1900 exists. Now, its time to get the password hash for the user who has the ‘uId’ value of 1900. One way to determine the characters of the password is to use ASCII comparison, and we will start with the first character of the password column using the following payload:

DWRAction.getUserRightsByRoleID("1 or (select ascii(substr(password,1,1)) from TBL_USERS where id = 1900) between [ASCII value] and [ASCII value]");

The ASCII values can be obtained from the chart below:


This part gets repetitive as the trial and error process begins. After many tries using elimination by tweaking the ‘between’ ASCII values, we now know the first character is an ASCII value of 49 which is equivalent to ‘1’. Repeat this tedious process till the n character by altering the substr() parameter in the statement. You can make the process less manual by using Burp Intruder and observing the responses.

Update: After knowing that its a hash value from the first few characters, the statement can be simplified to:

DWRAction.getUserRightsByRoleID("1 or (select ascii(substr(password,1,1)) from TBL_USERS where id = 1900) = [ASCII value]");

Where the ASCII value can range from 48 to 57 and 97 to 122. (Thanks @gifted88 for the tip.)

After all the hard work, the password hash for the user with “uId” 1900 is:


Post-Exploitation with Windows PowerShell

I gave a presentation on the topic of “Post-Exploitation with Windows PowerShell” sometime ago in 2015. This presentation showcases the use of PowerShell scripting exploitation frameworks coupled with various penetration testing tools and AV evasion techniques for post-exploitation on compromised hosts.

The slides can be found here:

Video of the demo from the presentation:

Trà Đá Hacking #1- FRP Remote Root

I recently spoke at Trà Đá Hacking #1 (, organized by VNSec in HCMC on the topic of ‘How to Get Started in Finding 0-Days – A Use Case’. The short introductory talk was intended to introduce the topic of finding 0-days using a real-world scenario as an example.

The slides can be found here:

I have created a remote code/command execution exploit client for the talk, based on 2 vulnerabilities discovered last year. The exploit can be downloaded here:

A sample video of the exploit in action:

Update: After a long time, the guys at FRP finally released the fixed version: