Thursday, December 30, 2010

Bruter 1.1 final

Finally, it is out :). I can do it before new year.

The new things since beta2 are (not including bug fixes)
- HTTP digest authentication
- SIP protocol

Happy new year ;)

Update (4 Jan 2011):
Sorry, I did a mistake again in binary. I included the wrong openssl file ("libssl32.dll" instead of "ssleay32.dll"). If you have a problem when starting my app. Try to download it again.

Thursday, December 23, 2010

Excel RC4 Encryption Algorithm

I played a wargame. There is a protected xls file. I could not find a free tool to break it. When I tried to use their trial tools, there is a instant recovery feature. I wonder how they do it. I decided to read the encryption algorithm. I knew the default Office 2003 encryption algorithm is RC4. After some searching, I found the Microsoft Document

The first link is how the RC4 key is generated and what is stored in xls file. The second link is what contents to be encrypted.

After understanding the Excel password, I code the python for testing the password Here is the important part.
def gen_excel_real_key(pwd, salt):
    h0 = hashlib.md5(pwd).digest()
    h1 = hashlib.md5((h0[:5] + salt) * 16).digest()
    return h1[:5]
def test_pass(pwd, salt, verifier, verifierHash):
    real_key = gen_excel_real_key(pwd, salt)
    key = hashlib.md5(real_key + '\x00\x00\x00\x00').digest()
    dec = rc4_crypt(key, verifier + verifierHash)
    if hashlib.md5(dec[:16]).digest() == dec[16:]:
        print "valid pass"
        print "invalid pass"

"salt", "verifier", and "verifierHash" can be extracted from FILEPASS record in Excel file. Can you see it? The "real_key" is only 5 bytes (40 bits). If you can find this key, no need to use password. The key space of real_key is 240. It is possible to do brute forcing. But is it easier than brute forcing password?

Compare it to alphanumeric password case insensitive. The key space of 8 characters is 368 = (32+4)8 = (25 + 4)8 > 240.

Another problem of brute forcing real_key, rc4 is slow compared to md5. I tried it with my simple C code. I get about 800,000 key/sec with 1 thread on Intel Core2 Q8300 2.5GHz. It takes about 16 days with 1 thread to try the whole key space. With GPUs, real_key is possible to be cracked in a few minutes.

What can we do when we get the real_key? There is the tool named guaexcel. The demo version allows you to use any real_key to decrypt any Excel file.

MS Word is the same as MS Excel. Just change the stream name from "Workbook" to "worddocument" stream. Then use tool named guaword to decrypt the Word file.

PS: If I have time, I will optimize the code and release it for free :). But do not expect it to be fast as commercial one.

Tuesday, December 7, 2010

Use OllyDbg to find ROP gadgets

I just tried writing a exploit with ROP technique. When I searched a tool to help me finding gadgets, I found only Immunity Debugger with pvefindaddr.

But I never used it. I am lazy to learn it now (I will later). I knew msfpescan with regex option can help me but it is too difficult. Then I tried with OllyDbg. I found a nice feature to help me finding gadgets. Here what I found

There is a search for sequence of commands when right click on CPU windows. Then it shows a dialog for typing assembly.

In this search dialog, we can use special commands and keywords. Below are what I excerpt from OllyDbg help "Search for a sequence of commands"
- R8, R16, R32 for any 8, 16, 32 bit register respectively.
- CONST for any constant
- JCC for any conditional jump
- ANY n for any 0..n commands

"Search for sequence of commands" find only one block. It is so inconvenient for us to choose a gadget. So I will show only "Search for all sequence" (the second red line of the first pic).

Let try with common gadget used for pivoting esp. I put "add esp,CONST;ANY 6;ret" to search "add esp" in ntdll.dll (on my Windows XP SP3). Here the results.

Assume We are interested "add esp, 74". Just double click it, we will see the assembly block like above pic. Then we can check if it is usable. As the above assembly code, we can use it if eax is zero.

I think this feature is nice. But I can search in only one executable module at a time :(. If someone know how to search in all modules, please tell me.;)

Sunday, November 21, 2010

Bruter 1.1 beta2 is out

Same as title, it is out. The new things (since 1.1 beta1) are

- SSH use less secure algorithm for key exchange (little faster)
- Able to use big wordlist file but still <2GB
- Save/Resume testing in "Save/Load config" (I'm lazy to add a new menu)
- Option to iterate passwords for each username (Password First)

I planned to add a new protocol on this version. But I change my mind because I'm lazy :S.

Have fun :)

Note on 23 Nov 2010:
Sorry for my mistake. I created the wrong directory name and zip filename "Bruter_1.2_beta2" (version 1.2 O_o). The actual name must be "Bruter_1.1_beta2". Fixed it.

Saturday, September 11, 2010

LEET MORE CTF 2010 write up - Lottery

This is the second and last challenge that I had time to play. I solved it :).

The challenge random 39 digits and give you a number of participant. You have to put the correct random number to win this lottery. When you put a wrong number, you get the correct random number.

I had put a lot of wrong number to see random numbers.
Here is the result when a number of participant is 391441 - 391447
Here is the result when a number of participant is 391449 - 391455
With these numbers, I saw a pattern. Let me added some white-spaces.
7568 83 670921640125823051707628843433985
7569 82 681967192314311352115314173149185
7570 81 693012744351683925071170856026113
7571 80 704058296540172225478856185741313
7572 79 715103848577544798434712868618241
7573 78 726149400614917371390569551495169
7574 77 737194952803405671798254881210369
See it? The first part (4 digits) is decreased by one. The second part (next 2 digits) is increased by one.

I also tried to make the next 2 digits as another part that is increased by one. But I saw
7576 75 759286057029266545161796893802497
7577 74 770331609066639118117653576679425
It is increased by 2.

Then I tried the diff of each digits in last part (last 33 digits). I found they (first 5 digits of last part) are changed like "(prev + x[i]) mod 10" or "(prev + x[i] +1) mod 10". It looks like a sum up of previous number. Then I tried to diff them.
681967192314311352115314173149185 - 670921640125823051707628843433985 = 11045552188488300407685329715200
693012744351683925071170856026113 - 681967192314311352115314173149185 = 11045552037372572955856682876928
704058296540172225478856185741313 - 693012744351683925071170856026113 = 11045552188488300407685329715200
715103848577544798434712868618241 - 704058296540172225478856185741313 = 11045552037372572955856682876928
I got about 4 numbers from diffing them. But there are only 2 that I got very often. They are

Then, I used
as my magic number and used the pattern I found for guessing. After a few tries, I got
You are realy lucky!!
Congratulations!! You win, send this WMcode to your bank: "C988EC4DC91EA4864FAA6B7D65030961B218D19CD96CF29DE28166F59B606158"
I won the lottery ;)

When the game end, hellman told the solution in IRC. Here is what he said
"in lottery random number generator was seeded with number of participants, and word 'uniform' points to erlangs random uniform, so just use erlang to guess the number"

LEET MORE CTF 2010 write up - Oh Those Admins!

This challenge is SQL injection. The line of problem is (I cannot remember the exact column and table name)
$r = mysql_query("SELECT login FROM admin WHERE password='" . md5($_GET['password'], true) . "'");
Normally, web sites keep the password hash as hex string. But this challenge keep raw hash (binary 16 bytes) in database. Binary can be string. So To bypass login, the output of md5 has to look like
' or 1=1#
The above string is what I thought first. But it is too long. To make the brute forcing fast, the required output string should be short. After checking from MySQL doc, I could make it shorter. Here is the list what I found
'or 1#    <== no need for space after single quote, any non-zero number is TRUE
'||1#     <== || is same as OR, no need for space after ||
Before writing the code for brute forcing, I remembered I can put non-printable characters in password using '%XX'. So 1 byte can be 256 values. No charset.

My code for brute forcing is

Ran it about 20 minutes on my slow windows pc, then got it.

password: 34b854c8
password: %34%b8%54%c8
result: c13e807082277c7c36231ed0dd34a863
result: ม>€p‚'||6# ะ4จc

Note: on linux, compile with "gcc -O2 -lssl -o prog prog.c" Note: If you want to see result from my program quick, run it with "./prog 4 4 200"

Submit it from url bar
,then got the real admin hash. It is "071CC0720D0ABD73F61A291224F248D6". But I could not reverse admin hash :( so poor me. When searched in google, I found it in hashkiller but it was not solved.

Below is not my solution

Before finished this post, I found other 2 writeups of this challenge. Very nice solutions. They found shorter SQL injection string.

First is The SQL injection string is
Here is the modification of my code: Got the result in a few minutes.
password: 2c55c819
password: %2c%55%c8%19
result: 3157e727097c7c27342e7dc2729f75ed
result: 1W็'    ||'4.}ยrŸuํ
Second is The SQL injection string is
Here is the modification of my code: Got the result in a second.
password: 22a80f
password: %22%a8%0f
result: 047f1f9ed77f467a273d279d8e521422
result:   žืFz'='ŽR "

Thursday, August 12, 2010

Hide OpenSSH Debian Distribution Version on Ubuntu

From the previous post about OpenSSH banner on Ubuntu, I just found the easy method to hide the distribution version in Ubuntu 10.04 by chance.

The Ubuntu 10.04 has the new option for sshd_config. That is "DebianBanner". You just have to add "DebianBanner no" line (without quotes) into sshd_config, then restart it. Your OpenSSH banner will change from "SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu3"

Normally, Ubuntu ports code from Debian. So I checked the option in Debian as well. I found the option in Debian 6 as well.

PS: The option has been added since Ubuntu 10.04.

Sunday, August 1, 2010

PHP magic_quotes_gpc and SQL injection

เรื่อง magic_quotes_gpc ของ PHP อาจจะดูเก่าไปหน่อย เพราะ feature นี้ใน PHP 5.3 ก็ deprecated แล้ว และก็จะไม่มีใน PHP 6 แต่ผมก็ยังเชื่อว่าหลายๆ คนยังไม่เข้าใจว่า magic_quotes_gpc มันช่วยและไม่ช่วยป้องกัน SQL injection ยังไง

ตาม document ของ PHP ถ้า magic_quotes_gpc ได้ถูกเปิดใช้งาน ตัวอักษร ' (single-quote), " (double-quote), \ (backslash) และ NULL ในข้อมูล GET, POST, COOKIE จะถูก escape ด้วย backslash อัตโนมัติ

กรณีที่ 1 SQL injection กับ MySQL
ผมขอเริ่มด้วยกรณีที่ magic_quotes_gpc ช่วยป้องกัน เรื่อง SQL injection ตัวอย่างนี้  PHP programmer ทุกๆ คนน่าจะเคยเห็นแล้ว
นั่นคือ code สำหรับการทำ authentication โดยข้อมูลอยู่ใน MySQL database (สมมติว่าเก็บ password เป็น plaintext)
// ... initialization and establishing database connection ...
$result = mysql_query("SELECT * FROM account WHERE username='".$_GET['username']."' AND password='".$_GET['password']."'");
if (mysql_num_rows($result) > 0) {
  // authenticated
else {
  // login failed
ทดสอบด้วย classic SQL injection คือ username เป็น
และ password เป็น
' or 1=1# 
ถ้าปิด magic_quotes_gpc จะได้ SQL command เป็น
SELECT * FROM account WHERE username='admin' AND password='' or 1=1#'
หลังเครื่องหมาย # ใน MySQL คือ comment จะไม่รวมใน SQL command และโดยทั่วไป (รวมถึง MySQL) operator precedence ของ AND จะสูงกว่า OR นั่นคือ AND จะถูกคิดก่อน OR ถ้าใส่วงเล็บให้ดูง่ายก็จะเป็น
SELECT * FROM account WHERE (username='admin' AND password='') or 1=1
เนื่องจาก 1=1 เป็นจริงเสมอ ก็ทำให้ SQL command ของข้างบนเท่ากับ
SELECT * FROM account
ซึ่งก็จะ login ผ่าน :)

ในทางกลับกัน ถ้าเปิด magic_quotes_gpc จะได้ SQL command เป็น
SELECT * FROM account WHERE username='admin' AND password='\' or 1=1'
ก็จะ login ไม่ผ่าน :(

จะเห็นว่า ถ้าจะทำ SQL injection ในกรณีนี้ต้องมีการใช้ single-quote เข้าไปใน username หรือ password แต่ magic_quotes_gpc จะ escape single-quote ทำให้ไม่สามารถทำ SQL injection หรือพูดได้ว่า
magic_quotes_gpc ป้องกัน SQL injection ได้ ถ้า SQL injection ต้องใช้ตัวอักษร ' (single-quote), " (double-quote), \ (backslash) หรือ NULL

กรณีที่ 2 SQL injection กับ database อื่นๆ
ผมขอใช้ code เดิมจากกรณีที่ 1 แต่เปลี่ยน database เป็นตัวอื่นที่ escape single-quote ตาม SQL standard (เช่น Oracle, MSSQL) คือใช้ single-quote สองตัว เช่น 'O''hh' แต่ครั้งนี้ password ต้องเปลี่ยนเป็น
' or 1=1-- 
(-- คือ comment ตาม SQL standard)
คราวนี้ขอแสดงเฉพาะกรณีเปิด magic_quotes_gpc ก็จะได้ SQL command เป็น
SELECT * FROM account WHERE username='admin' AND password='\' or 1=1--'
สำหรับ database ที่ escape single-quote ตาม SQL standard จะมองว่า password คือ \ (backslash) ซึ่งก็จะ login ผ่านอยู่ดี :)

หลายคนอาจจะบอกว่า PHP กับ MSSQL ไม่น่าจะมีคนใช้ ส่วน Oracle ก็คงมีใช้น้อยมาก อันนี้ผมเห็นด้วย แล้ว PostgreSQL ละ
PostgreSQL ตอนแรกก็ใช้ backslash เพื่อที่จะ escape single-quote แต่ใน version 8 ก็จะมี option standard_conforming_strings ให้เลือกใช้แบบเดิม (สำหรับ backward compatibility) หรือแบบ SQL standard โดยที่ทาง PostgreSQL เองก็แนะนำให้ใช้ตาม standard

Note: PHP มี option magic_quotes_sybase เพื่อที่จะ escape single-quote สำหรับ database ที่ทำตาม standard

กรณีที่ 3 SQL injection กับข้อมูลที่เป็นตัวเลข
ตัวอย่างที่พบบ่อยๆ ของกรณีนี้ คือการแสดงข้อมูลตาม id ที่อยู่ใน database โดยที่ PHP code ที่ทำ SQL query เป็นดังนี้
mysql_query("SELECT col1, col2, col3 FROM data WHERE id=".$_GET['id']);
ทดสอบด้วยการใส่ค่า id เป็น
0 UNION SELECT username,password,1 FROM account 
จะได้ SQL command เป็น
SELECT col1, col2, col3 FROM data WHERE id=0 UNION SELECT username,password,1 FROM account
สมมติว่าใน database ไม่มี id ที่เป็น 0 SQL command ก็จะเป็น
SELECT username,password,1 FROM account
ทำให้แทนที่จะดึงข้อมูลจาก data table ก็กลายเป็นดึงข้อมูลมาจาก account table มาแสดงผล

จะเห็นว่า id เป็นตัวเลข และโดยส่วนมาก programmer ไม่ได้ใส่ single-quote สำหรับตัวเลข ดังนั้น SQL injection ไม่ต้องมี single-quote ก่อนที่จะเพิ่ม SQL command และ magic_quotes_gpc ก็ไม่สามารถช่วยป้องกันในกรณีได้ เพราะไม่มีตัวอักษรที่ต้อง escape

ตัวอย่างอื่นๆ ของ SQL injection กับข้อมูลที่เป็นตัวเลข คือเวลาแสดงข้อมูลเป็นหลายๆ หน้า ตัวเว็บจะมีการรับหน้า และจำนวนข้อมูลต่อหน้า

กรณีที่ 4 SQL injection กับ SQL operator
กรณีนี้ส่วนมากจะพบในหน้า Advanced Search ของเว็บที่ให้ผู้ใช้สามารถใส่หลายๆ เงื่อนไขในการค้นหา และสามารถเลือกได้ว่าจะนำมา AND หรือ OR โดยที่ทางเว็บส่ง AND หรือ OR ตรงๆ เช่น
HTML code snippet:
col1: <input name="col1" type="text" /><br/>
<input checked="checked" name="oper" type="radio" value="AND" /> และ
<input name="oper" type="radio" value="OR" /> หรือ<br/>
col2: <input name="col2" type="text" />
PHP code snippet:
$result = mysql_query("SELECT col1, col2, col3 FROM data WHERE col1='%".$_POST['col1']."%' ".$_POST['oper']." col2='%".$_POST['col2']."%'");
จากตัวอย่างผู้ใช้สามารถเงื่อนไขของ col1 กับ col2 และมี radio box เพื่อเลือก AND หรือ OR โดยตัวอย่าง SQL injection ก็คือแก้ไขค่า oper เป็น
UNION SELECT username,password,1 FROM account#
ทำให้ SQL command เป็น
SELECT col1, col2, col3 FROM data WHERE col1='%cond1%' UNION SELECT username,password,1 FROM account# col2='%cond2%'
ซึ่งก็คือค้นหาข้อมูลใน data table ตาม cond1 และข้อมูลใน account table ทั้งหมด

Note: ถ้า SQL command ใน PHP code ซับซ้อนกว่านี้ การทำ SQL injection ก็ยังเป็นไปได้ แค่อาจต้องเพิ่มบางส่วน เช่น วงเล็บ เพื่อให้ SQL syntax พร้อมกับทำ injection

จะเห็นว่ากรณีนี้ ก็เป็นอีกกรณีหนึ่งที่ magic_quotes_gpc ไม่สามารถป้องกัน SQL injection ได้

จากตัวอย่างทั้งหมดที่กล่าวมา คงทำให้เห็นกันแล้วว่า magic_quotes_gpc ช่วยและไม่ช่วยป้องกัน SQL injection ยังไง (จริงๆ คือนึกไม่ออกแล้ว)
ถ้ามีการเปิด magic_quotes_gpc ก็ยังสามารถทำ SQL injection ถ้าไม่จำเป็นต้องใช้ตัวอักษรที่ถูก escape
และก็จะจบก็คงต้องเขียนวิธีป้องกัน ที่มีอยู่หลักเดียวง่ายๆ คือตรวจสอบ หรือ filter input ที่มาจากผู้ใช้ทั้งหมด ก่อนนำมาประมวลผล

วิธีการป้องกันตามตัวอย่างที่ได้ยกมา (สมมติว่าไม่มีการใช้ magic_quotes_gpc)
1. สำหรับข้อมูลที่เป็นตัวอักษร ใช้
ก่อนที่ใช้นำไปใส่ใน SQL command
2. สำหรับข้อมูลตัวเลข ให้ทำการ cast เป็นตัวเลขก่อนด้วยการใช้
3. สำหรับข้อมูลที่เป็นส่วนหนึ่งของ SQL syntax ก็ให้ตรวจสอบว่าเป็นที่เราอนุญาตหรือไม่ เช่นตัวอย่างในกรณีที่ 4 ก็ตรวจสอบว่าค่า oper เป็น AND หรือ OR เท่านั้น

สุดท้ายการที่ทาง PHP จะเอา magic_quotes_* ออกนั้น น่าจะเป็นเพราะว่า (อันนี้ความคิดผม) มันทำให้ programmer เข้าใจผิด และอาจทำให้สับสน รวมทั้งข้อมูลที่รับมาอาจจะไม่ได้เอามาใช้กับ database เช่น save ลงไฟล์ ทำให้ programmer ต้องมาแก้ไขข้อมูลให้ถูกต้องอีกด้วย

Tuesday, July 13, 2010

Identified Ubuntu version from SSH banner

Many times seeing the SSH full banner, I have to googling it for the OS version. I decided I should collect them here.

Let me note a little knowledge about banner (identification string in RFC).

Following RFC 4253 (section 4.2. Protocol Version Exchange), after connection established, the client and server MUST send an identification string. The identification string MUST be

SSH-protoversion-softwareversion SP comments CR LF

The 'comments' string is OPTIONAL. Below are example of valid identification string:
- SSH-2.0-OpenSSH_5.1p1 Debian-5ubuntu1
- SSH-2.0-PuTTY_Release_0.60

To make it easy, I call the identification string as "banner". From the example banners, they are very easy to guess the product name and version. The banner can be for more useful.

As told in title, the default banner can be used for identified Linux distribution and version. For example, the banner "SSH-2.0-OpenSSH_5.1p1 Debian-5ubuntu1" is identified as Ubuntu 9.04. However, of course, it's work only if administrator installs the binary package from its own distribution.

Before going to a list of SSH banner and OS version, I want to say it again "the client and server MUST send an identification string". It means you can identify OS of client and server.

Here is the list that I checked
Ubuntu 5.10:  SSH-2.0-OpenSSH_4.1p1 Debian-7ubuntu4
Ubuntu 6.04:  SSH-2.0-OpenSSH_4.2p1 Debian-7ubuntu3[.x]
Ubuntu 7.04:  SSH-2.0-OpenSSH_4.3p2 Debian-8ubuntu1[.x]
Ubuntu 7.10:  SSH-2.0-OpenSSH_4.6p1 Debian-5ubuntu0[.x]
Ubuntu 8.04:  SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu1[.x]
Ubuntu 8.10:  SSH-2.0-OpenSSH_5.1p1 Debian-3ubuntu1
Ubuntu 9.04:  SSH-2.0-OpenSSH_5.1p1 Debian-5ubuntu1
Ubuntu 9.10:  SSH-2.0-OpenSSH_5.1p1 Debian-6ubuntu2
Ubuntu 10.04: SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu3
Ubuntu 10.04.1: SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu4
Ubuntu 10.04.2: SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu{5,6}
Ubuntu 10.04.3: SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu7
Ubuntu 10.10: SSH-2.0-OpenSSH_5.5p1 Debian-4ubuntu{4,5}
Ubuntu 11.04: SSH-2.0-OpenSSH_5.8p1 Debian-1ubuntu3
Ubuntu 11.10: SSH-2.0-OpenSSH_5.8p1 Debian-7ubuntu1

Why only Ubuntu?
Because I know Ubuntu upgrade OpenSSH only when they release a new version. I do not know other distributions.

But I also list other default banners :)
Debian 4:  SSH-2.0-OpenSSH_4.3p2 Debian-9
Debian 5:  SSH-2.0-OpenSSH_5.1p1 Debian-5
Debian 6:  SSH-2.0-OpenSSH_5.5p1 Debian-6

FreeBSD 7.0:  SSH-2.0-OpenSSH_4.5p1 FreeBSD-20061110
FreeBSD 7.2:  SSH-2.0-OpenSSH_5.1p1 FreeBSD-20080901
FreeBSD 8.0:  SSH-2.0-OpenSSH_5.2p1 FreeBSD-20090522
FreeBSD 8.1:  SSH-2.0-OpenSSH_5.4p1 FreeBSD-20100308
FreeBSD 9.0:  SSH-2.0-OpenSSH_5.8p2_hpn13v11 FreeBSD-20110503

openSUSE 10.0: SSH-2.0-OpenSSH_5.0
openSUSE 10.1: SSH-2.0-OpenSSH_5.1
openSUSE 10.2: SSH-2.0-OpenSSH_5.2

CentOS 4.x:  SSH-2.0-OpenSSH_3.9
CentOS 5.x:  SSH-2.0-OpenSSH_4.3
CentOS 6.x:  SSH-2.0-OpenSSH_5.3

Fedora 12:  SSH-2.0-OpenSSH_5.2
Fedora 13:  SSH-2.0-OpenSSH_5.4
Fedora 14:  SSH-2.0-OpenSSH_5.5
Fedora 15:  SSH-2.0-OpenSSH_5.6
Fedora 16:  SSH-2.0-OpenSSH_5.8
It seems the distributions that derived from RedHat show only OpenSSH version and no "pX" after version.

Friday, July 9, 2010

Bruter 1.1 beta1 is out

I'm back. Bruter 1.0 final was released about 2 months ago.

Today, I just want to say 1.1 beta1 is out. Download it from

If anyone want to see the awesome change (I hope) from new version, try SMB service. :D