Over the Wire/natas

[OverTheWire:Natas] natas 26 → 27 / natas27

ruming 2021. 8. 30. 14:02

 

소스코드

<?

// morla / 10111
// database gets cleared every 5 min 


/*
CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);
*/


function checkCredentials($link,$usr,$pass){
 
    $user=mysql_real_escape_string($usr);
    $password=mysql_real_escape_string($pass);
    
    $query = "SELECT username from users where username='$user' and password='$password' ";
    $res = mysql_query($query, $link);
    if(mysql_num_rows($res) > 0){
        return True;
    }
    return False;
}


function validUser($link,$usr){
    
    $user=mysql_real_escape_string($usr);
    
    $query = "SELECT * from users where username='$user'";
    $res = mysql_query($query, $link);
    if($res) {
        if(mysql_num_rows($res) > 0) {
            return True;
        }
    }
    return False;
}


function dumpData($link,$usr){
    
    $user=mysql_real_escape_string($usr);
    
    $query = "SELECT * from users where username='$user'";
    $res = mysql_query($query, $link);
    if($res) {
        if(mysql_num_rows($res) > 0) {
            while ($row = mysql_fetch_assoc($res)) {
                // thanks to Gobo for reporting this bug!  
                //return print_r($row);
                return print_r($row,true);
            }
        }
    }
    return False;
}


function createUser($link, $usr, $pass){

    $user=mysql_real_escape_string($usr);
    $password=mysql_real_escape_string($pass);
    
    $query = "INSERT INTO users (username,password) values ('$user','$password')";
    $res = mysql_query($query, $link);
    if(mysql_affected_rows() > 0){
        return True;
    }
    return False;
}


if(array_key_exists("username", $_REQUEST) and array_key_exists("password", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas27', '<censored>');
    mysql_select_db('natas27', $link);
   

    if(validUser($link,$_REQUEST["username"])) {
        //user exists, check creds
        if(checkCredentials($link,$_REQUEST["username"],$_REQUEST["password"])){
            echo "Welcome " . htmlentities($_REQUEST["username"]) . "!<br>";
            echo "Here is your data:<br>";
            $data=dumpData($link,$_REQUEST["username"]);
            print htmlentities($data);
        }
        else{
            echo "Wrong password for user: " . htmlentities($_REQUEST["username"]) . "<br>";
        }        
    } 
    else {
        //user doesn't exist
        if(createUser($link,$_REQUEST["username"],$_REQUEST["password"])){ 
            echo "User " . htmlentities($_REQUEST["username"]) . " was created!";
        }
    }

    mysql_close($link);
} else {
?>

database는 5분마다 초기화된다고 한다.

username을 입력했을 때 user가 존재하지 않으면 username과 password를 database에 저장한다.

user가 존재하면, checkCredentials 함수로 username과 password를 검사해 일치하면 저장된 username과 password를 출력, 일치하지 않으면 wrong password ~ 이하를 출력한다.

 

mysql_real_escape_string 함수는 아래의 특수문자 앞에 백슬래시를 붙여 sql injection을 방어한다.

' " \ \x00 \n \r 1x1a(EOF)

우회 방법 : %a1~%fe와 같은 값을 입력하면 백슬래시를 하나의 문자로 취급해 우회할 수 있다고 한다.

https://securitynote.tistory.com/3

 

htmlentities 함수는 HTML 문자 엔티티에 존재하는 모든 문자를 엔티티로 변환한다.

html_entity_decode()로 디코딩할 수 있다고 한다.

 

mysql_num_rows 함수란 데이터베이스에서 쿼리를 날려서 나온 레코드들의 개수를 카운트하는 함수로

값이 없으면 0, 있으면 레코드 개수를 반환한다.

 

username과 password가 일치할 경우

guest/guest로 user를 생성해보았다. 다시 로그인하면 다음과 같이 username과 password가 뜬다.

 

mysql_real_escape_string함수를 우회해 sql injection을 시도하는 거라고 생각했는데, 앞부분에 놓친 게 있었다.

CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);

주석으로 공개한 database의 정보를 보면 각각 길이가 64로 설정되어 있다.

natas28의 뒤에 공백을 포함해 64보다 긴 문자열을 전달해보자.

natas28                                                                                                1

위와 같이 natas28 + 공백 + 1을 username에 넣고 password에는 1을 넣었다.

 

natas28 1이 생성되었다고 한다.

 

이제 다시 natas28과 1로 로그인해보자.

 

natas28로 로그인에 성공하였다!

JWwR438wkgTsNKBbcJoowyysdM82YjeF