3
0

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 1 year has passed since last update.

CTF初心者のためのログ分析問題解法

Last updated at Posted at 2022-12-18

本CTFの問題の原データは 現時点では公開されておりません。
公開された場合にはリンク等を追加します。

はじまり

この記事は

Sendai CTF 2022 での問題の一つである、ログ分析問題について、CTF初心者である私の復習を兼ねたいわゆるWrite UPです。一応復習を兼ねているのでかなり冗長に、かつハンズオンのように実行過程を辿れるよう書いています。

一応こんな人に

  • これから知識ほぼ0でCTFに挑む人
  • ログファイルの中身を見たことない人
  • grep awkとかなにそれ?おいしいの?ってくらいの人
  • 「はっきんぐたのしそー( ᐛ👐)」 みたいな人
  • 少しだけプログラミングができる人

全て私のことです。(うわっ…私の技術力、低すぎ…?)

環境

ProductName:	macOS
ProductVersion:	12.5.1
BuildVersion:	21G83

実行はzshにて、Pythonは3系です。

問題

ログ分析問題

この大問は小問5つから構成され、アクセスログについてそれぞれ解答するものです。
アクセスログは以下のような構成になっています。

access_log
[2020-06-19 10:09:11+0900] XXX.XXX.XXX.XXX YYY.YYY.YYY.YYY:PP "GET /manager/html HTTP/1.1" 401 1032 R0VU************************************************************************************************************************

問題データの公開は認められていないため、部分的に情報を隠しています。
(当然ですが)CTF本番で配布されたデータでは具体的な値が記載されています。
XXX.XXX.XXX.XXXYYY.YYY.YYY.YYY:PPはIPアドレスやポート番号を、R0VU*****...はbase64でエンコードされた各種情報です。

このようなアクセスログが 36000行以上 あるファイルaccess_logを解析します。

問題1

問題

ログファイル中の /manager/html への偵察行為の対象となるアプリケーション名を答えてください。

解答例

Googleは偉大なり。
https://www.google.com/search?q=%2Fmanager%2Fhtml
トップに「攻撃ログ」と気になる文字列があるので開いてみると、どうやらtomcatというアプリケーションへのアクセスらしいです。

tomcatとはWebサーバにおいて動かすソフトウェアみたいなものです。
詳しくはGoogleなりYahooなりに聞いてください。

FLAGはtomcatになります。

問題2

問題

ログファイルから一番多く偵察を受けたアプリケーション名を答えてください。

解答例

問題1から類推して、HTTPリクエスト( "GET /manager/html ..." )に注目すると良さそうです。これをなんとかして36000行以上のデータに対して集計する必要があります。
awk sort uniqコマンドを使うと良いらしいようです。(私はCTFの途中までこのコマンドを知りませんでした…)

とりあえずこの問題ではHTTPリクエストの部分だけ抜きだせれば良いので

% awk '{print $6}' access_log

を実行すると、出力(途中の抜粋)は

...
/TP/public/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
/TP/public/index.php?s=captcha
/users?page=&size=5
/
/
/hudson
/
/
/manager/html
/manager/html
...

こんな感じに出てきます。

awkコマンドでは各行の6番目の要素($6)を表示(print)しています。

[2020-06-19 10:09:11+0900] XXX.XXX.XXX.XXX YYY.YYY.YYY.YYY:PP "GET /manager/html HTTP/1.1" 401 1032 R0VU************************************************************************************************************************
1           2              3               4                  5    6             7 ...

といったような数え方です。(スペースで区切っています。区切り文字を変えるオプションもあるようです。)

次は重複をまとめた上で数えていきます。先ほどの出力に

% awk '{print $6}' access_log | sort | uniq -c

を実行すると出力(最初と最後3行ずつ)は

   1 '/script1.sh'
 535 /
   3 /.git/config
...
   1 www.facebook.com:443
   2 www.google.com:443
   1 www.ipip.net:443

となります。行頭の値はそれぞれ重複する数です。

sortでは、それぞれの出力をソートします。
uniqでは入力で隣り合う行が同じ内容の場合にまとめます。直前でsrotするのはこのためです。
uniq -cではその重複を数えて一緒に表示します。
またコマンドA | BではAの出力をBの入力として実行するようです。(この辺は私は詳しくないので有識者の方教えてください。。。)

ただこの出力も膨大な行数になります。目視で最大値を見つけても良いのですがもう少し賢く見つけましょう。はい、sortします。

% awk '{print $6}' access_log | sort | uniq -c | sort

この出力(最初と最後3行)はこんな感じです。

   1 '/script1.sh'
   1 /.well-known/security.txt
   1 /AuPV
...
   2311 /wp/wp-login.php
   2316 /cms/wp-login.php
   2326 /wp1/wp-login.php

これでほぼ答えに辿り着きました。あとは問題1同様'/wp1/wp-login.php'で検索すると出てきます。
FLAGはWordPressになります。

問題3

問題

ログファイル中の2020/06/19~2020/06/20の/manager/html へのアクセス件数を答えてください。

解答例

とりあえず該当期間のデータを抽出する必要がありそうです。grepしましょう。

grepコマンドとは入力された文字列を含む行を出力するコマンドです。例えば、

% grep /wp1/wp-login.php access_log

を実行すると/wp1/wp-login.phpへのアクセスログのみを全て出力します。

ただ、このときに日付は2020-06-19 2020-06-20の2つあり、単純なgrepでは実現できません。
ちょっと工夫して

% grep "2020-06-\(19\|20\)" access_log

とすると、最後の(19|20)19または20のどちらかということになります。
\()|が文字列として認識されるのではなく、演算子として認識されるための記号です。正規表現です。

このコマンドの結果も膨大な量になります。確認するのが面倒なので

% grep "2020-06-\(19\|20\)" access_log | (head -1; tail -1)

を実行すると最初と最後の1行だけを表示してくれます。[参考文献]
こうすると出力は、

[2020-06-19 00:11:34+0900] xxx.xxx.xxx.xxx blank:80 "GET / HTTP/1.1" 200 False R0VU********************
[2020-06-20 23:49:19+0900] yyy.yyy.yyy.yyy YYY.YYY.YYY.YYY:80 "GET / HTTP/1.0" 200 False R0VU****************************************************************************************************

となります。問題なさそうです。

次は/manager/htmlへのアクセスを抽出にgrepします。

% grep "2020-06-\(19\|20\)" access_log | grep "/manager/html"

あとは数えるだけです。wcコマンドを使って

% grep "2020-06-\(19\|20\)" access_log | grep "/manager/html" | wc

出力は

     676    6760  249955

となり、676(=アクセス数)であることがわかりました。

wcコマンドでは入力に対してline, word, byteの順で出力されます。

FLAGは676になります。

懺悔

この記事の著者はこの問題を力技で解いたなどと供述しています。具体的にはテキストエディタにて手作業で

  1. 該当期間外のデータを削除し、(文字列2020/06/19を含む最初の行を検索してそれより前の行を全て削除、2020/06/20より後も同様)
    902行目以前の行を全て削除して...↓
    grep_delete_m.jpg

  2. /manager/htmlを検索して該当数を数えました。
    grep_search_m.jpg
    grep_lined_m.JPG

非常に反省しています。

問題4

問題

ログファイル中の /manager/html へのアクセスで、一番多く試行されたパスワードを調べ、その試行回数を答えてください。

解答例

パスワードどこ?????? って最初はなるかと思います。なりましたよね?
例として、一番最初に挙げたログをもう一度見てみます。

[2020-06-19 10:09:11+0900] XXX.XXX.XXX.XXX YYY.YYY.YYY.YYY:PP "GET /manager/html HTTP/1.1" 401 1032 R0VU************************************************************************************************************************

どこ??????
ただ、1箇所だけ内容がわからないところがあります。そうです、最後のR0VU...のところです。ここは一見するとランダムな文字列ですが、base64と呼ばれる変換をした文字列になっています。
この部分をデコード(変換を元に戻すこと)すると

GET /manager/html HTTP/1.1
Connection: close
Authorization: Basic dG9tY2F0OmJvdGg=
User-Agent: Java/1.8.0_131
Host: XXX.XXX.XXX.XXX
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

意味のありそうな文字列が出てきます。
(XXX.XXX.XXX.XXXのところは隠していますが具体的な値が入っています。)

この中にパスワードの隠し場所なりそうな箇所は... Authorization しかなさそうです。ここもbase64になってそうな雰囲気があるのでデコードすると

tomcat:both

アアアアアアアアってなりましたか?私はなりました。
他のログについても同様に解析してみると、admin:tomcatのような出力があります。adminはユーザ名に使われることが多く、:の前に書いていることからもユーザ名:パスワードの順になっている気がします。

これでやっとパスワードを見つけることができたのでFLAG発見 ...にはならず、全てのログに対して同様の処理を施し、パスワードを見つけて、最も多いものを探さなければなりません。
ただこれまでの問題を解いてきたので簡単にできるはずです。
まずは全てのログをデコードするところから。

% grep "/manager/html" access_log | awk '{print $10}' | nkf -mBW 

出力は長いので省略しますが、全ての該当するログに対してデコードできました。

nkf -mBWはデコードするコマンドです。
なおnkfはhomebrewなどでインストールが必要です。

ここからAuthorizationを抽出し、

% grep "/manager/html" access_log | awk '{print $10}' | nkf -mBW | grep "Authorization"
Authorization: Basic YWRtaW46
Authorization: Basic YWRtaW46YWRtaW4=
Authorization: Basic YWRtaW46MTIzNDU=
...
Authorization: Basic dGVzdDphc2RmZ2hqa2w=
Authorization: Basic YWRtaW5pc3RyYXRvcjpxcTEyMzQ1Ng==
Authorization: Basic YWRtaW5pc3RyYXRvcjphc2RmZ2hqa2w=

この第3成分を抽出してデコードすると...

% grep "/manager/html" access_log | awk '{print $10}' | nkf -mBW | grep "Authorization" | awk '{print $3}' | nkf -mBW
admin:admin:adminadmin:12345admin...(中略)...qq123456administrator:asdfghjkl%

全て1行にまとめて出してきやがります。 そう簡単には思い通りになってくれないのがプログラミングです。このままでは最も多く試行されたパスワードは分かりません。

もうコマンドラインで無理に済ませようとするのは諦めてpythonに逃げましょう。
一度デコードすべきデータをテキストファイルに保存します。

% grep "/manager/html" access_log | awk '{print $10}' | nkf -mBW | grep "Authorization" | awk '{print $3}' > userid_password_encoded.txt

COMMAND > FILENAMEを用いることで、COMMAND実行結果をFILENAMEに記録することができます。
試しに

% cat userid_password_encoded.txt

を実行すると、

YWRtaW46
YWRtaW46YWRtaW4=
YWRtaW46MTIzNDU=
...
dGVzdDphc2RmZ2hqa2w=
YWRtaW5pc3RyYXRvcjpxcTEyMzQ1Ng==
YWRtaW5pc3RyYXRvcjphc2RmZ2hqa2w=

確かに記録されていることがわかります。

あとはpythonのコードですが、

python decode_password.py
import base64

with open('./userid_password_decoded.txt', 'w', encoding='UTF-8') as w:
    with open('./userid_password_encoded.txt') as f:
        for line in f:
            w.write(base64.b64decode(line).decode()) # ここでデコードして
            w.write('\n') # ここで改行を入れている

こうすることで改行を入れます。
そろそろゴールです。pythonを実行し、出力ファイルuserid_password_decoded.txtを集計して

% python3 decode_password.py
% awk -F ':' '{print $2}' userid_password_decoded.txt | sort | uniq -c | sort

awk -F ':':を区切り文字とする、という意味です。

出力は

   1 12345
   6 
  17 role1
...
  22 admin
  22 manager
  22 tomcat

長かったですがついにフラグに辿り着きました。
FLAGは22になります。

私はログ分析の問題群の中で唯一、この問題4をCTFの時間内に解くことができませんでした。

% grep "/manager/html" access_log | awk '{print $10}' | base64 -d

を実行すると途中までは良いのですが、あるところでなぜか

Invalid character in input stream.

となります。(この理由をご存知のつよつよエンジニアの方は教えて下さい。)

問題5

問題

ログファイルから一日のアクセス件数が一番多い日を調べ、その日の中で一番アクセス数が多い送信元IPアドレスを答えてください。

解答例

これまでの問題を解いてきたのであれば、なんとなく解法が見えてきます。
まずはアクセス件数が一番多い日を調べます。

% awk '{print $1}' access_log | sort | uniq -c | sort
  82 [2020-06-14
  95 [2020-06-15
  99 [2020-06-17
 116 [2020-06-22
 243 [2020-06-16
 383 [2020-06-18
 899 [2020-06-19
13896 [2020-06-20
20447 [2020-06-21

これより2020/06/21について調べると良さそうです。
次はこの日の送信元IPアドレスを調べます。ただそれぞれのログにはIPアドレスが2つあります。今回のCTFでは誤答しても何もないので両方試しても良いのですがどちらが送信元かを考えます。
私もアクセスログは初めて見たのでわからなかったのですが、後者のIPアドレスにはポート番号がついていることから、アクセス先アドレスではないかと推測しました(詳しい人教えて下さい)。
とりあえず前者のIPアドレスについて調べると

% grep "2020-06-21" access_log | awk '{print $3}' | sort | uniq -c | sort
...
 351 185.128.41.50

これよりFLAGは185.128.41.50になります。

この記事は終了しました

扱ったコマンドの一覧

  • 検索エンジン:偉大。
  • awk:1行における任意のn番目の要素を抽出しました。
  • sort:出力を順に並べました。
  • uniq:重複を解消したり数えたりしました。
  • grep:特定の文字列を含む行を抽出しました。
  • wc:行を数えました。
  • nkf:base64などの変換をしました。

最後に

筆者はアホなのでCTF中にメモを取るのを忘れており、そのためFLAGが違うかもしれません。
最後になりますがSendaiCTFの運営の方々ありがとうございました。
稚拙な文章と低すぎる技術力で構成されているこの記事が運営の方々に見つからないことを祈ります。

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?