SISS/Natas,LOS, XSS

Natas 19 →20

3190024 2020. 7. 8. 01:44

 일반 사용자로 로그인되었다고 한다. 관리자로 로그인하라고 한다.

function debug($msg) { /* {{{ */
    if(array_key_exists("debug", $_GET)) {
        print "DEBUG: $msg<br>";
    }
}
/* }}} */

 debug(): $_GET에 "debug"라는 key가 있다면 $msg를 출력한다.

function print_credentials() { /* {{{ */
    if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
    print "You are an admin. The credentials for the next level are:<br>";
    print "<pre>Username: natas21\n";
    print "Password: <censored></pre>";
    } else {
    print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.";
    }
}
/* }}} */

/* we don't need this */
function myopen($path, $name) { 
    //debug("MYOPEN $path $name"); 
    return true; 
}

/* we don't need this */
function myclose() { 
    //debug("MYCLOSE"); 
    return true; 
}

 print_credentiaols(): $_SESSION이 존재하고 &_SESSION에 "admin"이라는 key가 존재하고 그 값이 1이라면(참이라면?) admin이라는 메시지와 함께 비밀번호를 출력한다.

그 외의 경우에는 일반 사용자로 로그인되었음을 메시지로 출력한다.

function myread($sid) { 
    debug("MYREAD $sid"); 
    if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
    debug("Invalid SID"); 
        return "";
    }
    $filename = session_save_path() . "/" . "mysess_" . $sid;
    if(!file_exists($filename)) {
        debug("Session file doesn't exist");
        return "";
    }
    debug("Reading from ". $filename);
    $data = file_get_contents($filename);
    $_SESSION = array();
    foreach(explode("\n", $data) as $line) {
        debug("Read [$line]");
    $parts = explode(" ", $line, 2);
    if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
    }
    return session_encode();
}

myread(): 

 debug("MYREAD $sid"); $_GET에 "debug"라는 key가 있다면 "MYREAD $sid"를 출력한다.

 

 strspn(string, charlist, start[=option], length[=option]): string에서 charlist에 존재하지 않는 지점을 반환한다.

 

 만약 $sid에 포함된 문자의 종류의 수가 $sid의 길이와 같지 않으면 "debug"라는 키의 존재 여부에 따라 "Session file

 doesn't exist"를 출력한 뒤, ""를 반환한다.

 

 session_save_path(): session data를 저장하기 위해 사용되었던 현재 디렉토리의 경로를 반환한다.

 

 $filename에 현재 디렉토리의 경로에 "/"와 "mysess_"와 $sid를 이어붙인 문자열을 저장한다.

 만약 $filename에 해당하는 파일이 존재하지 않는다면 "debug" key의 존재 여부에 따라 "Session file doesn't exist"를

 출력하고 ""를 반환한다.

 

 debug("Reading from ". $filename); "debug" key의 존재 여부에 따라 "Reading from " 과 $filename을 이어붙인 문자

 열을 출력한다.

 

 file_get_contents(): 전체 파일을 문자열로 읽어들여 반환한다.

 

 $data = file_get_contents($filename); $filename이라는 이름의 파일을 문자열로 읽어들여 $data에 저장한다.

 

 foreach(array_expresiion as $value){ statement; } : 배열의 각 값에 대하여 반복 작업을 한다.

 (출처: https://www.codingfactory.net/10700)

 explode( delimiter, string [, limit] ): 왼족부터 차례대로 문자열을 분할할 기준, 분할할 문자열, 분할할 개수(정수, 옵션)

 을 정한다.

 즉, $data를 "\n"을 기준으로 자른 것들에 대해 debug("Read [$line]");을 실행하고, $parts에 $line을 공백을 기준으로

 둘로 나눈 것을 저장하는데, 이 때는 배열 형태로 저장되는 것 같다.

 

 만약 $parts[0]이 ""가 아니라면, $_SESSION[$parts[0]]에 $parts[1]을 저장한다.

 마지막으로 session_encode()를 호출하여 반환한다.

function mywrite($sid, $data) { 
    // $data contains the serialized version of $_SESSION
    // but our encoding is better
    debug("MYWRITE $sid $data"); 
    // make sure the sid is alnum only!!
    if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
    debug("Invalid SID"); 
        return;
    }
    $filename = session_save_path() . "/" . "mysess_" . $sid;
    $data = "";
    debug("Saving in ". $filename);
    ksort($_SESSION);
    foreach($_SESSION as $key => $value) {
        debug("$key => $value");
        $data .= "$key $value\n";
    }
    file_put_contents($filename, $data);
    chmod($filename, 0600);
}

mywrite():

 debug()를 이용하여 상황에 따라 $sid와 $data를 출력한다.

 만약 $sid에 숫자와 알파벳대소문자, 그리고 "-"에 포함되지 않는 지점과 $sid의 길이가 다르다면 함수를 종료한다.

 $filename에 세션을 저장하기 위한 현재의 디렉토리와 "/", "mysess_", $sid를 차례로 이어붙인 문자열을 저장한다.

 

 $data에 ""를 저장한다.

 debug()함수를 이용하여 $filename을 출력한다.

 

 ksort(): 연관배열에 들어 있는 키를 기준으로 오름차순으로 정렬한다.

 (출처: https://m.blog.naver.com/PostView.nhn?blogId=diceworld&logNo=220190287072&proxyReferer=https:%2F%2Fwww.google.com%2F)

 $_SESSION의 키들을 오름차순으로 정렬한다.

 

 foreach($array as $key => $value): key와 value를 가져온다.

 debug()를 이용하여 $key와 $value를 출력하고 $data에 $key와 $value와 "\n"을 이어붙인다.

 

 file_put_contents(파일경로, 입력할 내용)

 $filename에 해당하는 파일이 없는 경우 파일을 작성하여 $data의 내용을 파일에 쓴다.

 

 chmod(): 파일의 모드를 변환한다.

 chmod($filename, 0600): $filename이라는 파일으 모드를 변환한다.

 권한의 종류에는 읽기(4), 쓰기(2), 실행(1)이 있고, 권한부여 그룹은 소유자, 그룹, 모든 사용자 순이다.

  0600을 해석해보면,

 맨 앞의 0은 8진수임을 나타낸다. 0을 빼고 나타내면 동작에 오류가 발생할 수 있다.

 6은 소유자에 대한 권한으로, 읽기(4)와 쓰기(2)를 더한 값으로, 읽기와 쓰기 권한을 부여한다.

 나머지 두 숫자는 모두 0인데, 이는 그룹과 모든 사용자에 그 어떠한 권한도 주지 않음을 알 수 있다.

 (출처: https://devjhs.tistory.com/212)

/* we don't need this */
function mydestroy($sid) {
    //debug("MYDESTROY $sid"); 
    return true; 
}
/* we don't need this */
function mygarbage($t) { 
    //debug("MYGARBAGE $t"); 
    return true; 
}

session_set_save_handler(
    "myopen", 
    "myclose", 
    "myread", 
    "mywrite", 
    "mydestroy", 
    "mygarbage");
session_start();

 session_set_save_handler(open, close, read, write, destroy, gc)

 open: 열기 함수.  

 close: 닫기 함수.

 read: 읽기 함수. 항상 문자열 값을 반환해야 한다. 

 write: 쓰기 함수. 출력 스트림이 닫힐 때까지 실행되지 않는다.

 destroy: 파괴 핸들러. 세션이 파괴될 대 실행되고, 세션 id를 인수로 받는다.

 gc: 쓰레기 수거자. 세션 쓰레기 수거가 실행될 때 실행되고, 최대 세션 수명을 인수로 받는다.

(출처: http://www.lug.or.kr/files/docs/PHP/function.session-set-save-handler.html)

 

 session_start(): 세션 사용 전에 세션을 초기화 한다.

if(array_key_exists("name", $_REQUEST)) {
    $_SESSION["name"] = $_REQUEST["name"];
    debug("Name set to " . $_REQUEST["name"]);
}

 $_REQUEST에 "name"이라는 key가 있다면 $_REQUEST["name"]의 값을 $_SESSION["name"]에 저장하고, debug함수를 이용하여 적절한 값을 출력한다.

print_credentials();

$name = "";
if(array_key_exists("name", $_SESSION)) {
    $name = $_SESSION["name"];
}

?>

 print_credentials()함수를 호출한다.

 $name을 ""로 초기화 한다.

 $_SESSION에 "name"이라는 key가 있다면 그 value를 $name에 저장한다.

 

먼저 $_GET에 debug가 있어야 하므로 url 뒤에 debug, name을 추가한다.

myread함수는 파일을 "\n" 단위로 자른 뒤, 줄마다 다시 공백을 기준으로 잘라 첫 번째 덩어리를 $_SESSION의 key로, 두 번째 덩어리를 그 value로 저장한다.

mywrite함수는 $_SESSION의 key들을 오름차순으로 정렬한 뒤에 각 key와 value를 공백으로 구분짓고, "\n"을 파일에 작성한다.

$_SESSION에 debug와 name을 저장할 때 name에 개행문자(%0a)와 공백문자(%20)를 이용하여 admin%0aadmin%201 을 저장하면  mywrite 함수에서 파일에 name과 admin%0aadmin%201 을 각각 key와 value로 저장할 것이고, myread함수에서는 admin%0aadmin%201을 개행문자를 기준으로 자른 뒤에 개행문자 뒤의 admin을 $_SESSION의 key로, 공백문자 뒤의 1을 $_SESSION["admin"]의 value로 저장할 것이다.

?debug=true&&name=admin%0aadmin%201 을 입력한다.

natas21 비밀번호: IFekPyrQXftziDEsUr3x21sYuahypdgJ