Home OverTheWire: NATAS 30

OverTheWire: NATAS 30

This is another basic login form. The unique thing about this one is the sourcecode is in PERL.

use CGI qw(:standard);
use DBI;

print <<END;
Content-Type: text/html; charset=iso-8859-1

<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas30", "pass": "<censored>" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">

<!-- morla/10111 <3  happy birthday OverTheWire! <3  -->

<div id="content">

<form action="index.pl" method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="login" />

if ('POST' eq request_method && param('username') && param('password')){
  my $dbh = DBI->connect( "DBI:mysql:natas30","natas30", "<censored>", {'RaiseError' => 1});
  my $query="Select * FROM users where username =".$dbh->quote(param('username')) . " and password =".$dbh->quote(param('password')); 

  my $sth = $dbh->prepare($query);
  my $ver = $sth->fetch();
  if ($ver){
    print "win!<br>";
    print "here is your result:<br>";
    print @$ver;
    print "fail :(";

print <<END;
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>

From quick examination of the source, it’s clear that we’re intended to do SQL injection here. In fact, we don’t even have to extract the password, it should be given to us if we are able to get ANY successful result from the query. So a very simple SQL injection will do. Problem is, and the central challenge here, is the quote() function that’s supposed to be sanitizing our inputs. There must be a known way around quote().

A few minutes of searching Google and I found the answer I was looking for. Turns out param() from CGI.pm introduces a vulnerability to quote(). From the post:

You see, param is context-sensitive. In scalar context, if the parameter has a single value (name=foo), it returns that value, and if the parameter has multiple values (name=foo&name=bar) it returns an arrayref. In list context, it returns a list of values, whether there are zero, one, or many. The argument list of a method (such as quote) is a list context. That means that someone using your app can cause quote to receive two values, and quote‘s optional second argument is an SQL data type that the first argument should be treated as. If the data type is a non-string type like NUMERIC, then quote will pass its first argument through without any quoting. This constitutes an opportunity for SQL injection.

To exploit the vuln, simply send username=natas31&password=”or 1=1&password=2 in the POST data.

This post is licensed under CC BY 4.0 by the author.