
EBS スナップショットから、ログインできない EC2 の役割を無停止で調べてみる
はじめに
かつまたです。
運用を引き継いだ AWS 環境で、用途のわからない EC2 インスタンスが見つかることがあります。ログインに必要な鍵やパスワードが手元になく、SSMエージェントも入っていないと、OS の中を直接確認できません。止めてよいかどうかもわからないため、再起動を伴う調べ方も選べません。
そこで、EBS スナップショットから作ったボリュームを別の EC2 に読み取り専用でつなぎ、ファイルとログから役割を調べました。
やりたいこと
- 対象の EC2 を止めず、OS にもログインせずに、次のことを調べる
- 何のサーバーか(入っているソフトウェア、動いているサービス、設定)
- 最後に人が使ったのはいつか(ログオン履歴)
- 定期的に何をしているか(スケジュールされたタスク、cron)
検証環境
用途がわからないサーバーの代わりに、検証用の EC2 を 2 台用意し、それらしい役割を仕込みました。
- Windows Server 2022: コンピューター名 FILESV01。7-Zip を入れ、DNS サーバーにセカンダリゾーンを作り、夜間に動くスケジュールタスクを登録しました
- Ubuntu 24.04: nginx を入れ、
/etc/cron.dに定期処理を置きました
スナップショット取得前に、Windows には Fleet Manager のリモートデスクトップで、Ubuntu には EC2 Instance Connect の SSH でログインを何回か試行しました。
やってみた
1. スナップショットを作成する
対象の EC2 は動かしたまま、ルートボリュームのスナップショットを作成します。
aws ec2 create-snapshot --volume-id <対象のボリューム ID> \
--description "offline investigation" \
--tag-specifications 'ResourceType=snapshot,Tags=[{Key=Name,Value=investigation}]'
2. スナップショットからボリュームを作り、調査用 EC2 につなぐ
aws ec2 create-volume --snapshot-id <スナップショット ID> \
--availability-zone <調査用 EC2 の AZ> --volume-type gp3
aws ec2 attach-volume --volume-id <作成したボリューム ID> \
--instance-id <調査用 EC2 の ID> --device /dev/sdf
3. 読み取り専用でマウントする
デバイス名は環境によって変わるので、つないだあと lsblk -f で確認しました。
Windows(NTFS)は ntfs-3g でマウントしました。
sudo mkdir -p /mnt/win && sudo mount -t ntfs-3g -o ro /dev/nvme1n1p1 /mnt/win
Linux(ext4)は、noload を付けずにマウントし、ジャーナルを適用してから読みました。
sudo mkdir -p /mnt/linux && sudo mount -o ro,noexec,nosuid,nodev /dev/nvme2n1p1 /mnt/linux
4. Windows
Windows の情報は、主にレジストリのファイル(ハイブ)とイベントログに残っています。
| 知りたいこと | 場所 |
|---|---|
| OS の版、インストール日 | Windows/System32/config/SOFTWARE の Microsoft\Windows NT\CurrentVersion |
| コンピューター名 | SYSTEM の ControlSet001\Control\ComputerName\ComputerName |
| インストール済みのソフトウェア | SOFTWARE の Microsoft\Windows\CurrentVersion\Uninstall(32 ビット版は WOW6432Node\...) |
| 自動起動するサービス | SYSTEM の ControlSet001\Services(Start が 0〜2 で、ImagePath が Windows 標準以外のもの) |
| スケジュールされたタスク | Windows/System32/Tasks 配下の XML(UTF-16) |
| ログオン履歴 | Windows/System32/winevt/Logs/Security.evtx のイベント ID 4624 |
| リモートデスクトップの接続履歴 | Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx のイベント ID 21(ログオン)・25(再接続) |
| DNS サーバーとしての設定 | SOFTWARE の Microsoft\Windows NT\CurrentVersion\DNS Server\Zones |
レジストリは hivexregedit で書き出せます。まず、インストール済みのソフトウェアを見ました。
hivexregedit --export /mnt/win/Windows/System32/config/SOFTWARE \
'Microsoft\Windows\CurrentVersion\Uninstall' | grep '"DisplayName"'
文字列の値の多くは、そのままでは読めない形で出力されます(長いので途中で省略しています)。
"DisplayName"=hex(1):37,00,2d,00,5a,00,69,00,70,00,20,00,32,00,34,00,2e,00,30,00,39,00,20,00,28,00,78,00,36,00,...
hex(1)(文字列)や hex(2)(環境変数を含む文字列)は、UTF-16LE のバイト列を 16 進数で並べたものです。次のスクリプト(regdec.py)を通して文字列に戻しました。
#!/usr/bin/env python3
# hivexregedit の hex(1)/hex(2)/hex(7) を UTF-16LE の文字列に戻す
import sys, re
def dec(m):
b = bytes.fromhex(m.group(2).replace(',', '').replace('\\', ''))
return '"' + b.decode('utf-16le', 'ignore').replace('\x00', '|').strip('|') + '"'
for line in sys.stdin:
print(re.sub(r'hex\((1|2|7)\):([0-9a-f,\\]+)', dec, line.rstrip('\n')))
hivexregedit --export /mnt/win/Windows/System32/config/SOFTWARE \
'Microsoft\Windows\CurrentVersion\Uninstall' | python3 regdec.py | grep '"DisplayName"'
"DisplayName"="7-Zip 24.09 (x64 edition)"
"DisplayName"="Microsoft Visual C++ 2022 X64 Minimum Runtime - 14.44.35211"
"DisplayName"="AWS PV Drivers"
"DisplayName"="Microsoft Visual C++ 2022 X64 Additional Runtime - 14.44.35211"
"DisplayName"="aws-cfn-bootstrap"
"DisplayName"="Amazon SSM Agent"
"DisplayName"="Amazon EC2Launch"
DNS サーバーのゾーンの設定です。
hivexregedit --export /mnt/win/Windows/System32/config/SOFTWARE \
'Microsoft\Windows NT\CurrentVersion\DNS Server\Zones' | python3 regdec.py \
| grep -E '^\[|"Type"|"MasterServers"'
[\Microsoft\Windows NT\CurrentVersion\DNS Server\Zones\corp.example.internal]
"MasterServers"="192.0.2.10"
"Type"=dword:00000002
corp.example.internal がセカンダリゾーン(Type=2)で、上流のサーバーは 192.0.2.10 です。
スケジュールされたタスクは、Windows/System32/Tasks 配下にタスクごとのファイルがありました。NightlyShareBackup の定義は UTF-16 の XML なので、iconv で UTF-8 にしてから読みました。
iconv -f UTF-16 -t UTF-8 /mnt/win/Windows/System32/Tasks/NightlyShareBackup \
| grep -E '<(Command|Arguments|StartBoundary|UserId)>'
<StartBoundary>2026-10-31T01:00:00Z</StartBoundary>
<UserId>SYSTEM</UserId>
<Command>powershell.exe</Command>
<Arguments>-File C:\scripts\nightly-backup.ps1</Arguments>
毎日 1:00 に、SYSTEM 権限で C:\scripts\nightly-backup.ps1 を実行するタスクなことを確認しました。
#!/usr/bin/env python3
# evtx のレコードを、ファイルヘッダーのチャンク数に頼らず全チャンクから読む
# 使い方: logons.py Security.evtx -> 4624 のうち LogonType 10・7
# logons.py <LocalSessionManager の evtx> lsm -> イベント ID 21・25
import sys, re
from Evtx.Evtx import ChunkHeader
def data(xml, name):
m = re.search(r'<Data Name="%s">([^<]*)</Data>' % name, xml)
return m.group(1) if m else ''
def tag(xml, name):
m = re.search(r'<%s[^>]*>([^<]*)</%s>' % (name, name), xml)
return m.group(1) if m else ''
buf = open(sys.argv[1], 'rb').read()
lsm = len(sys.argv) > 2
rows = set()
for m in re.finditer(b'ElfChnk\x00', buf):
for rec in ChunkHeader(buf, m.start()).records():
xml = rec.xml()
eid = tag(xml, 'EventID')
t = re.search(r'SystemTime="([^"]+)"', xml).group(1)
if lsm and eid in ('21', '25'):
rows.add((t, eid, tag(xml, 'User'), tag(xml, 'Address')))
elif not lsm and eid == '4624' and data(xml, 'LogonType') in ('10', '7'):
rows.add((t, data(xml, 'LogonType'), data(xml, 'TargetUserName'), data(xml, 'IpAddress')))
for r in sorted(rows):
print(*r)
python3 logons.py /mnt/win/Windows/System32/winevt/Logs/Security.evtx
2026-10-31 13:52:18.017977 10 Administrator ::1
2026-10-31 13:53:22.100441 7 Administrator ::1
LogonType 10(リモートデスクトップ)でログオンし、LogonType 7(切断したセッションへの再接続)を確認できました。
Fleet Manager のリモートデスクトップは SSM エージェント経由でインスタンスの中から接続するため、接続元は ::1 になります。通常の RDP であれば、ここにクライアントの IP アドレスが入ります。
リモートデスクトップの接続は、TerminalServices-LocalSessionManager のイベント ID 21・25 でも確認しました。
python3 logons.py \
"/mnt/win/Windows/System32/winevt/Logs/Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx" lsm
2026-10-31 13:52:20.972511 21 FILESV01\Administrator ::%16777216
2026-10-31 13:53:21.805639 25 FILESV01\Administrator ::%16777216
2026-10-31 13:54:04.592886 25 FILESV01\Administrator ::%16777216
5. Linux
| 知りたいこと | 場所 |
|---|---|
| OS の版 | etc/os-release |
| ログイン履歴 | var/log/auth.log*、var/log/wtmp(last -f) |
| ユーザーごとの最終ログイン | var/log/lastlog(wtmp が空でも残っていることがある) |
| 定期処理 | var/spool/cron/crontabs/、etc/cron.d/、etc/systemd/system/*.timer |
| 自動起動するサービス | etc/systemd/system/multi-user.target.wants/ |
| 待ち受けているポート・アプリの設定 | etc/ssh/sshd_config、etc/nginx/ など |
定期処理は etc/cron.d に置かれていました。
cat /mnt/linux/etc/cron.d/reports-backup
30 2 * * * root /opt/reports/backup.sh
毎日 2:30 に、root で /opt/reports/backup.sh を実行していることがわかりました。
ログイン履歴も確認しました。
last -f /mnt/linux/var/log/wtmp
ubuntu pts/0 <接続元 IP> Sat Oct 31 14:09 - 14:09 (00:00)
ubuntu pts/0 <接続元 IP> Sat Oct 31 14:08 - 14:08 (00:00)
reboot system boot 7.0.0-1014-aws Sat Oct 31 13:45 still running
#!/usr/bin/env python3
# lastlog(1 レコード 292 バイト、ユーザー ID 順)からユーザーごとの最終ログインを読む
import sys, struct, datetime
root = sys.argv[1]
log = open(root + '/var/log/lastlog', 'rb').read()
for line in open(root + '/etc/passwd'):
name, _, uid = line.split(':')[:3]
off = int(uid) * 292
if off + 292 > len(log):
continue
t, tty, host = struct.unpack_from('<i32s256s', log, off)
if t:
when = datetime.datetime.fromtimestamp(t, datetime.timezone.utc)
print(name, when.isoformat(), tty.rstrip(b'\0').decode(), host.rstrip(b'\0').decode())
python3 lastlog.py /mnt/linux
ubuntu 2026-10-31T14:09:11+00:00 pts/0 <接続元 IP>
結果
| 知りたいこと | Windows | Linux |
|---|---|---|
| 何のサーバーか | 7-Zip、DNS サーバーのセカンダリゾーン(上流 192.0.2.10)、コンピューター名 FILESV01 | Ubuntu 24.04、ホスト名 web-reports、nginx(:80) |
| 最後に人が使った日時 | 13:52 の RDP ログオンと、その後の再接続 | ubuntu の SSH ログイン(14:08・14:09) |
| 定期的な処理 | NightlyShareBackup(毎日 1:00) | cron.d のバックアップ(毎日 2:30) |
対象の EC2 は止めず、OS にもログインせずに、役割・最後に使われた日時・定期的な処理を確認できました。
クラスメソッドオペレーションズ株式会社について
クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AIをフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、クラスメソッドオペレーションズ株式会社 コーポレートサイト をぜひご覧ください。
※2026年1月 アノテーション㈱から社名変更しました







