2022년도 HISCON(교외 중학생대회) 문제이지만 이제서야 올린다.. 뒷북
노션에 정리해놔서 걍 갭쳐해서 올림

flag: HSOC{D1d_you_u$e_bon3h5_4tt4ck?_W3ll_d0n3!}
사용기술: RSA (boneh durfee), sage
출제의도: rsa의 기본적인 원리에 대해 알고 주어진 문제를 보고 알맞은 공격기법을 찾아 플래그를 얻어낼 수 있는가를 확인하기 위해 출제하였다.

문제

prob.py

prob.txt

N = 0x7b15390f1a50e56ee97f4ba06a61776f6aa8e67ff088138446979029538308fbbe3d37353e891499167978398865a0d7f7e40d4237224ae503c2583265a845ac5b2ff984abb9d3ea74eb567d8e73a621d4f32ee5b25332f3c94e2fbcc18187ac129d4e66fc74fea10d984542c879331f34260aecacc387e978d95357187d19ab
e = 0x3e14358a7600bc551c4920f24590e3e51547b5c78d0f64622388d3d5e1d182fee6f7e066acb244e2f74e46934d5e0a5348c9404f881f0b546e1b74944b13624517b58bbddbbae4111a0367395bd3cccf4105f9530e84e3064b13851efba39f3c3a2fc3069a86d6e6b3bd039bcd7f782ca8e0f8decf81cec2569724baaa0d34ab
c = 0x13c0b26bac5e723cdbabe3b4742bea93380e7ee676a1a000af7daf6788931fc1f53ed76d32c4372d09204a53b206de972c0c267e2a291a513e8c932f172af1d4472f4cc12df67e824cb8037086935a99bd251f5bc9ccfe4f7726a88c093cab2869312ca40b2d02309ff5ddd86e2302c296ba9fa85490894d8e27c8ea54c7f1bc

 

문제 설명

RSA의 boneh durfee를 사용한 문제이다.
일반적으로 n과 e값이 비슷할 정도로 e값이 클 때 wiener’s attack을 쓴다고 알았는데 당시 boneh durfee를 사용할수도 있다는 것을 새롭게 알게되었다.
wiener’s attack보다 boneh durfee는 훨씬 덜 알려지고 생소하게 느껴지는 것 같아서 해당 공격기법을 선택했다.

2. 문제 제작

문제를 만들며 이미 구현되어있는 코드를 그대로 붙여넣기 해서 푸는건 딱히 좋은 문제가 아니라고 생각되어서 구현코드 내에 있는 델타값을 수정해서 알고리즘 적용범위를 늘려주도록 했다.

위 코드가 구현된 코드에 있는 N, e, delta, m 값이다.
delta: 전용 지수에 대한 예측 범위(d < n^delta)(기본값: 0.25)
또한 위 코드에 주석에 나와있듯이 m 값을 늘려줄수록 속도가 느려진다.
 

풀이

sol.sage

 
delta값 0.18을 0.25까지 늘리고 m값은 5로 설정해주었다.
온라인으로 실행 가능한 sage를 사용하여 코드를 실행하면 개인키 d가 구해진다.

그리고 구한 값들을 이용하여 평문을 구해보면 플래그가 나오게 된다.

from Crypto.Util.number import *

n = 0x7b15390f1a50e56ee97f4ba06a61776f6aa8e67ff088138446979029538308fbbe3d37353e891499167978398865a0d7f7e40d4237224ae503c2583265a845ac5b2ff984abb9d3ea74eb567d8e73a621d4f32ee5b25332f3c94e2fbcc18187ac129d4e66fc74fea10d984542c879331f34260aecacc387e978d95357187d19ab
e = 0x3e14358a7600bc551c4920f24590e3e51547b5c78d0f64622388d3d5e1d182fee6f7e066acb244e2f74e46934d5e0a5348c9404f881f0b546e1b74944b13624517b58bbddbbae4111a0367395bd3cccf4105f9530e84e3064b13851efba39f3c3a2fc3069a86d6e6b3bd039bcd7f782ca8e0f8decf81cec2569724baaa0d34ab
c = 0x13c0b26bac5e723cdbabe3b4742bea93380e7ee676a1a000af7daf6788931fc1f53ed76d32c4372d09204a53b206de972c0c267e2a291a513e8c932f172af1d4472f4cc12df67e824cb8037086935a99bd251f5bc9ccfe4f7726a88c093cab2869312ca40b2d02309ff5ddd86e2302c296ba9fa85490894d8e27c8ea54c7f1bc
d = 5448511435693918250863484721514292687178096328572373396537572878464059764348289027

m = pow(c,d,n)
print(long_to_bytes(m))

 

동아리 멘토링 할 때도 본인이 멘토면 자료는 직접 만들어서 합시다
이거 혹은 다른 멘토 자료 그대로 본인 블로그에 올리거나 자료로 만들어서 쓰지 말고.. 안 그럴거라 믿을게요

RSA

최초의 공개 키 암호화 방안 (Rivest-Shamir-Adleman)
암호화, 복호화에 다른 키를 사용 (Public key로 암호화, Private key로 복호화)
→ 비대칭키 암호
→ 공개키 암호
RSA 등장 후 1년 후 디피, 헬먼이 공개키 암복호화 개념 소개 → 공개키 서명 수행 x
소인수분해 이용
디지털 서명 구축에 사용됨
개인키 소유자 → 서명 가능
공개키 이용 → 서명의 유효성 확인
트랩도어 치환 개념을 활용

트랩도어 치환

수 $x$를 같은 범위의 수 $y$로 변환하는 함수
공개키를 이용해서 $x$로부터 $y$를 계산하기 쉬움
하지만 개인키를 모르면 $y$로부터 $x$를 계산하는게 사실상 불가능한 방식으로 변환한다는 특징
→ RSA의 경우 $x$가 평문, $y$가 암호문
 

개념1

유클리드 알고리즘

$$ GCD(A,B)\;=\;GCD(B,r) $$
: 주어진 두 수 사이에 존재하는 최대공약수(GCD)를 구하는 알고리즘
두 수 : $a, b \;\;(a>b)$
몫 : $q$
나머지 : $r$
위를 정리해보면 $a = b*q+r$
유클리드 알고리즘은 위 식에서
$a = b*q+r$
$b = b1*r+r1$
$b1 = b2*r1+r2$

위와 같은 과정을 거쳐 나머지인 $r_n$이 0이 되었을 때 $b_n$의 값이 두 수의 최대공약수라는 것
위 식을 이용해서 두 수의 최대공약수를 구한다면 다음과 같음
두 수 : 100, 16
100 = 16*6+4
16 = 4*4+0
원래 우리가 일반적으로 하는 방식인 소인수분해한 다음 최대공약수를 구하는것과 비교해보면 훨씬 간단함
100 = 225*5
16 = 222*2
최대공약수 = 2*2 = 4
 

베주 항등식

확장 유클리드 알고리즘 → 베주 항등식의 명제를 가정
두 정수와 그 최대공약수 사이 관계 보여주는 항등식
3가지 참인 명제
$GCD(a, b) = d$ 라고 할 때

  1. $ax + by = d$ 를 만족하는 정수 $x, y$가 존재
  2. $d$는 정수 $x, y$에 대하여 $ax + by$ 로 표현할 수 있는 가장 작은 정수
  3. $ax + by$ 로 표현될 수 있는 모든 정수는 $d$의 배수

증명

$$ S\;=\;ax\;+\;by>0\;|\;x,y\in ℤ $$
집합 $S$는 자연수의 부분집합, $S$가 공집합이 아닐 경우
자연수 정렬성 → “공집합이 아닌 양의 정수의 모든 집합은 최소 원소를 갖는다.”
 

확장 유클리드 알고리즘

유클리드 알고리즘을 확장하여 $a, b$의 최대공약수 뿐만 아니라 $ax + by = gcd(a, b)$를 만족하는 정수해 $x, y$를 찾는 알고리즘
ex) $gcd(240, 46) = c$ 라고 할 때 $240x + 16y = c$의 해와 $c$ 구하기

최대공약수는 $2,\;$$2$$240x + 46y = 2$를 만족하는 정수해 $x = -9, y = 47$
 
 

용어 설명

$gcd(a,b)$ : 정수 $a, b$의 최대공약수
$a\equiv b\;(mod\;m)$ : $a$와 $b$는 $m$에 의해 나누어졌을 때의 나머지가 동일
이 수식이 성립하면 $a-b$는 $m$의 정수배라 볼 수 있음
이때 $a=b\;mod\;m$ 과 $a \equiv b\; (mod\;m)$은 다름
$=$ 이 들어가는거는 계산 결과를 나타냄
ex) 2 = 17 mod 5
$\equiv$ 가 들어가는건 표현한다는거, 같다는 의미를 나타냄 (합동)
ex) 17 = 2 mod 5
 

개념2

비대칭키 암호화 : 비밀키로 공개키 알아내기 o, 공개키로 비밀키 알아내기 x
→ 이산대수 어려움 통해 구현
RSA는 소인수분해의 난해함을 기초로 보안 유지
두 소수: $p, q$
$p*q$ = 쉬움 ($k$)
$k$ = 소인수분해 어려움 ($p$=?, $q$=?)
$p, q$ : 페르마 소수 중 선택 (1024bit, 2048bit …)

F0 = 21 + 1 = 3
F1 = 22 + 1 = 5
F2 = 24 + 1 = 17
F3 = 28 + 1 = 257
F4 = 216 + 1 = 65537
F5 = 232 + 1 = 4294967297 = 641 × 6700417 (오일러, 1732)
F6 = 264 + 1 = 18446744073709551617 = 274177 × 67280421310721
F7 = 2128 + 1 = 340282366920938463463374607431768211457 = 59649589127497217 × 5704689200685129054721

큰 수는 컴퓨터로도 소인수분해 어려움
 

페르마의 소정리

$$ p가 \;소수이면,\;모든\;정수\;a에\;대해\;a^p\equiv a\;(mod\;p)\\ p가\;소수이고\;a가\;p의\;배수가\;아니면,\;a^{p-1}\equiv1\,(mod\;p) $$
위의 두번째 경우라면 $p$는 소수가 아님
모든 합성수 $p$에 대해 성립 안됨, 확률적으로 $p$가 합성수인데 소수로 판별 가능
페르마 소정리는 많은 합성수들이 상대적으로 시간이 오래 걸리는 완전한 소수 판별 x, 합성수인지 알려줌
페르마 소정리 → 소수(소수일 가능성 정수) → 다른 완전한 방법으로 소수인지 확인 → $p, q$로 사용
 

오일러 정리

페르마의 소정리를 일반화한 것 → 소수 p를 정수 n으로 확장한 형태
$$ a와\;n이\;서로\;서로소인\;양의\;정수일\;때,\\a^{\phi(n)}\equiv1\;(mod\;n) $$
 

개념3

나머지 연산 매우 자주 활용

m = a%b

$a^b\equiv m\;(mod\;N)$에서 $m$을 계산하는 일을 매우 자주 수행
→ 큰 수의 나머지를 빠르고 간단하게 계산하는 방법

 

나머지의 성질

정리 1

$$ ab\;mod\;N=(a\;mod\;N)(b\;mod\;N)\,mod\;N $$
→ 두 수의 곱의 나머지는 각각의 수의 나머지를 먼저 구하고 그 나머지를 곱한 수의 나머지를 구해도됨
 

지수법칙

$b=p+q$일 때 $a^b=a^{p+q}=a^p*a^q$
——>
$$ a^b\;mod\;N=a^pa^q\;mod\;N=(a^p\;mod\;N)(a^q\;mod\;N)\,mod\;N $$
 

정리 2

$$ a^b\;mod\;N=(a\;mod\;N)^b\;mod\;N $$
나머지를 먼저 계산해서 밑을 작게 만들고 거듭제곱해서 연산 용량 줄이기
 

동작

기호

$N$ : 암복호화 과정에 이용되는 modulus로 사용, $N=p*q$ 인 합성수
$p, q$ : 공개되지 않은 두 소수
$e$ : 공개된 지수
$d$ : $d*e\equiv 1\;mod\;(p-1)(q-1)$을 만족하는 공개되지 않은 자연수
$m$ : 암호화 전 평문 (메세지), 이때 m < N
$c$ : 암호문
$E$ : 암호변환함수 (Encrypt)
$D$ : 복호변환함수 (Decrypt)

키 생성

  1. 큰 소수 $p, q$를 생성
  2. $N=pq$를 계산
  3. $(p-1)(q-1)$과 서로소인 정수 $e$를 정함
    1. $\phi(N)$는 오일러 피 함수 : $N$과 서로소인 $N$ 이하의 자연수 개수
      1. ex) 8 이하의 양의 정수 중 8과 서로소인 수는 1, 3, 5, 7로 4개임. $\phi(6)=2$
    2. $N$이 소수인 경우 $\phi(N)=N-1$ (1부터 $p-1$까지 모두 $p$와 서로소이기 때문에)
    3. 곱셈적 함수
    4. $\phi(N) = \phi(pq)=\phi(p)\phi(q)=(p-1)(q-1)$
  4. $ed$를 $(p-1)(q-1)$로 나눈 나머지가 1인 정수 $d$를 계산 (확장 유클리드 알고리즘 사용)
    1. $ed\equiv 1\;(mod\;\phi(N))$를 만족하는 정수 $d$
    2. $ed\;\% \;(p-1)(q-1)= 1$
    3. $d \equiv e^{-1}(mod\; \phi(n))$
  • 키로 사용되지 않는 두 소수 $p, q$는 공개되면 안됨
    • 공개열쇠 $e$로부터 개인열쇠 $d$를 찾을 수 있음
  • 상황에 따라 3단계와 4단계의 순서를 바꿔서 개인열쇠를 먼저 정하고 공개열쇠를 생성함

참고

3단계에서 $e, (p-1)(q-1)$이 서로소여야 하는 이유 - 4단계 (서로소가 아니면 위 조건을 만족하는 $d$는 존재하지 않음)
4단계는 어떤 정수 $x$가 있을 때 다음과 같이 요약 가능
$$ ed=(p-1)(q-1)x\,+\,1 $$
 

암호화

$$ c=m^e\;mod\;N $$
평문을 나타내는 자연수를 e제곱한 결과를 N으로 나눠준것
→ $e, N$을 알면 누구든 암호화 가능
암호화키쌍 : $(e, N)$
 

복호화

$$ m=c^d\;mod\;N $$
$d, N$을 알면 누구든 복호화 가능
복호화키쌍 : $(d, N)$
 

복호화 증명

$m=c^d\;mod\;N$
$=(m^e\;mod\;N)^d\;mod\;N$
$=m^{ed}\;mod\;N$
$\to m^{ed} \equiv m\;(mod\;N)$
$m^{ed} \equiv m\;(mod\;N)$를 증명하면됨
$N=p*q$ 이므로 위 합동식이 $mod\;p$일 때 또는 $mod\;q$일 때 성립하면 $mod\;N$일 때도 성립함
$ed\equiv1\;(mod\;\phi(N))$이므로 적당한 정수 $k$가 존재하여 $ed=k*\phi(N)+1=k(p-1)(q-1)+1$이라 쓸 수 있음
 

1. 페르마의 소정리 이용

위 합동식에 대입하면
$m^{ed}\equiv m\;(mod\;N)$
$\to m^{ed}\equiv m \;(mod\;p)$
$\to m^{k(p-1)(q-1)+1} \equiv m\;(mod\;p)$
$\to m^{k(p-1)(q-1)} \,*\,m\equiv m\;(mod\;p)$
$\to (m^{p-1})^{k(q-1)}\,*\,m \equiv m\;(mod\;p)$
 

경우 1 - m이 p의 배수가 아닐 때

$m$이 $p$의 배수가 아니면 $p$가 소수이기 때문에 페르마의 소정리에 의해
$m^{p-1}\equiv 1\;(mod\;p)$이고 이 식을 위에 대입하면
$(m^{p-1})^{k(q-1)}\,*\,m \equiv m\;(mod\;p)$
$\to 1^{k(q-1)}\,*\, m \equiv m\;(mod\;p)$
$\to m \equiv m\;(mod\;p)$
이므로 합동식이 성립함
 

경우 2 - m이 p의 배수일 때

$m$이 $p$의 배수면 합동식 양변이 모두 $p$로 나누어 떨어짐 → 0이됨
따라서 합동식이 성립함
 

2. 오일러 정리 이용

$m \equiv c^d \;(mod\;n) \\ c^d \equiv (m^e)^d \equiv m^{ed}\; (mod \; n)$
오일러 정리를 이용하면 $ed = x\phi(n)\;+\;1$을 만족하는 자연수 $x$가 존재함. 따라서 다음 합동식이 성립함
$m^{ed} \equiv (m^{\phi(n)})^km\;(mod\;n)$
$(m^{\phi(n)})^km \equiv\;m\;(mod\;n)$


 

청소년부

cry - ROT

ROT47이라함

html 소스코드를 보면 다음과 같음

<!DOCTYPE Html />
<html>
 <body>
 <input type="text" name="flag" id="flag" value="enter the flag" />
 <input type="button" id="flag_dec" value="check-flag" />

 <script type="text/javascript">
    function dec(x) {
        var s=[];
        for(var i=0;i<x.length;i++) {
            var j=x.charCodeAt(i);
            if((j>=33)&&(j<=126)) {
                s[i]=String.fromCharCode(33+((j+ 14)%94));
            } else {
                s[i]=String.fromCharCode(j);
            }
        }
        return s.join('');
    }


    document.getElementById("flag_dec").onclick = function () {
        var flag = document.getElementById("flag").value;
        var enc = dec(flag)
            if ("2A@==@3LC~Ecf0u@CE*0DtGt?0C@Ecf0!2Dcf502&9N" == enc) {
                alert("Correct flag!");
            } else {
                alert("try again!");
            }
 }
 </script>
 </body>
</html>
2A@==@3LC~Ecf0u@CE*0DtGt?0C@Ecf0!2Dcf502&9N

 

위 문자열을 복호화해서 집어넣어야함

apollob{rOt47_FortY_sEvEn_rot47_Pas47d_aUh}

'ctf writeup' 카테고리의 다른 글

27회 해킹캠프 CTF writeup  (0) 2023.09.09
Pwnme qual 2023 writeup  (0) 2023.02.26
escape ctf 2023 writeup  (0) 2023.02.13
WaniCTF 2023 writeup  (0) 2023.02.13
Incognito 4.0 (ictf 2023)  (0) 2023.02.12

버스타서 운 좋게 2등을 했다.. 해킹캠프 올 때마다 팀원복이 넘 좋다(죄송스럽,,)

닉네임 12345로 참가해서 0솔브 문제 풀어서 1000점 겨우겨우 땀;; (+ 야매로 걍 운좋게 플래그 얻어걸림)

crypto 분야 문제가 하나도 없어서 놀랐고 대회 시간 동안 misc만 계속 시도함🥲 

 

USB! We can read it!

(usb.transfer_type == 0x01)&&(frame.len==35)

위와 같이 필터링을 걸어서 URB_INTERRUPT in으로 입력받은 것만 나오도록함

HID Data가 입력값인데 이때 이중에서 5~6자리 16진수가 키보드 자판의 입력값이됨

# pipe copy.txt
080000
080015 
000015
000000
000011
000000
000012
000000
000017
000000
000008
000000
000013
000013
000004
000000
000007
000000
000028
000000
010000
000000
020000
02000b
00000b
000000
000008
000000
00001c
000000
000036
000000
00002c
000000
020000
020009
000009
000000
00000c
000000
000011
000000
000004
000000
00000f
000000
00000f
00000f
00001c
000000
00002c
000000
00001c
000000
000012
000012
000018
000000
00002c
000000
00000e
000000
000011
000000
000012
000012
00001a
000000
00002c
000000
000017
000000
00000b
000000
000008
000000
00002c
000000
020000
020018
020018
020016
020000
020005
000005
000000
00002c
000000
000013
000000
000004
000000
000006
000000
00000e
000000
000008
000000
000017
000000
00002c
000000
000004
000000
000011
000000
000004
000000
00000f
00000f
00001c
000000
000016
000000
00000c
00000c
000016
000000
200000
20001e
200000
000000
000028
000000
000028
000000
020000
020017
000017
000000
00000b
000000
000008
000000
00002c
000000
000009
000000
00000f
000000
000004
000004
00000a
000000
00002c
000000
00000c
000000
000016
000000
00002c
000000
000010
000010
000007
000000
000022
000000
200000
200026
200000
000000
020000
02001a
00001a
000000
00000c
000000
000017
000000
00000b
000000
020000
02002d
020000
000000
00000a
000000
000015
000000
000008
000000
000004
000004
000017
000000
020000
02002d
020000
000000
000013
000000
000012
000000
00001a
000000
000008
000000
000015
000000
020000
02002d
020000
000000
000006
000000
000012
000000
000010
000000
000008
000000
000016
000000
020000
02002d
020000
000000
00000a
000000
000015
000000
000008
000000
000004
000000
000017
000000
020000
02002d
020000
000000
000015
000000
000008
000000
000016
000000
000013
000000
000012
000011
000000
000016
000000
00000c
000000
000005
000000
00000c
000000
00000f
000000
00000c
000000
000017
000000
00001c
000000
200000
200027
200000
000000
000028
000000
000028
000000
00002d
000000
020000
020000
000016
000000
000013
000000
00000c
000000
000007
000000
000008
000008
000015
000000
000010
000000
000004
000000
000011
000000

뒤에 필요없는 0은 다 자르고 필요한 값만 놔두면 다음과 같음

앞에 두자리가 02인 경우는 shift키를 눌러주는 형식임

원래 아래와 같은 코드가 작동해야함 (어디 블로그에서 긁어옴)

keyboard_code = {
    '01': '🌐🌐', #LCtrl
    '02': '✅✅', #LShift
    '04': 'aA',
    '05': 'bB',
    '06': 'cC',
    '07': 'dD',
    '08': 'eE',
    '09': 'fF',
    '0a': 'gG',
    '0b': 'hH',
    '0c': 'iI',
    '0d': 'jJ',
    '0e': 'kK',
    '0f': 'lL',
    '10': 'mM',
    '11': 'nN',
    '12': 'oO',
    '13': 'pP',
    '14': 'qQ',
    '15': 'rR',
    '16': 'sS',
    '17': 'tT',
    '18': 'uU',
    '19': 'vV',
    '1a': 'wW',
    '1b': 'xX',
    '1c': 'yY',
    '1d': 'zZ',
    '1e': '1!',
    '1f': '2@',
    '20': '3#',
    '21': '4$',
    '22': '5%',
    '23': '6^',
    '24': '7&',
    '25': '8*',
    '26': '9(',
    '27': '0)',
    '28': '\n\n',
    '29': '⛔⛔', #esc
    '2a': '💥💥', #BackSpace
    '2b': '\t\t',
    '2c': '  ',
    '2d': '-_',
    '2f': '[{',
    '30': ']}',
    '38': '/|',
    '39': '🆎🆎', #CapsLock
    '3a': '¹¹', #F1
    '4c': '💢💢', #delete
    '4f': '➡➡',
    '50': '⬅⬅',
    '51': '⬇⬇',
    '52': '⬆⬆'
}

f = open("pipe copy.txt","r")
lines = f.readlines()
f.close()

usb_data = list()
usb_data_tmp = ""
for i in lines:
    if i != "00:00:00:00:00:00:00:00\n" and i != usb_data_tmp:
        usb_data.append(i[:-1])
        usb_data_tmp = i

for i in range(len(usb_data)):
    print(usb_data[i])


flag = ""
x = 0
y = 0
copy_tmp = ""

for line in usb_data:
    data = line.split(":")
    if "01" in data[0]:
        print("Ctrl!")
        for i in range(0,8):
            if "2a" == data[i]:
                flag = flag[:-1]
            elif "00" != data[i]:
                flag += keyboard_code[data[i]][0:1]

    elif "02" in data[0]:
        print("Shift!")
        for i in range(0,8):
            if "2a" == data[i]:
                flag = flag[:-1]
            elif "00" != data[i]:
                flag += keyboard_code[data[i]][1:2]

    else:
        print("just input!")
        for i in range(2,8):
            if "2a" == data[i]:
                flag = flag[:-1]
            elif "00" != data[i]:
                flag += keyboard_code[data[i]][0:1]

print(flag)

근데 작동이 안되어서 그냥 리스트만 보고 알아서 때려맞춤. 그랬더니 다음과 같이 나옴

rrnoteppad HHey, Ffinallly yoou knoow the UUSBb packet anallysiiss! 

Tthe flaag is 
 
mmd5(Wwith_greaat_power_comes_great_responsibility)

-Sspideerman

괄호 안의 문자열을 md5로 바꾸면됨

그대로 md5로 바꿔도 안되길래 4가지 케이스를 세우고 3번째에 정답이 나옴

(Wwith_greaat_power_comes_great_responsibility)
78b048aef95312808588efb3bf34bb32

Wwith_greaat_power_comes_great_responsibility
72f32137a05f9953f41f6be6057e8b06

With_great_power_comes_great_responsibility
7f37b1af11faa23608c7c0a7aef10fa3

with_great_power_comes_great_responsibility
e5588c6a4dfef9cb519b0c20fd12cc4f
HCAMP{7f37b1af11faa23608c7c0a7aef10fa3}
아마 100퍼 문제 출제자분들의 의도는 이런게 아니였을거다.. 근데 실수도 실력인 것처럼 야매도 실력은 맞잖아여..?
걍 이렇게 푼 놈도 있구나 하고 웃어 넘기시길 ~

'ctf writeup' 카테고리의 다른 글

2023 APOLLO writeup  (1) 2023.10.31
Pwnme qual 2023 writeup  (0) 2023.02.26
escape ctf 2023 writeup  (0) 2023.02.13
WaniCTF 2023 writeup  (0) 2023.02.13
Incognito 4.0 (ictf 2023)  (0) 2023.02.12

동아리 멘토링 할 때도 본인이 멘토면 자료는 직접 만들어서 합시다
이거 혹은 다른 멘토 자료 그대로 본인 블로그에 올리거나 자료로 만들어서 쓰지 말고.. 안 그럴거라 믿을게요

RSA

최초의 공개 키 암호화 방안 (Rivest-Shamir-Adleman 수학자 3명의 이름 앞글자 따옴)
암호화, 복호화에 다른 키를 사용 (Public key로 암호화, Private key로 복호화)
→ 비대칭키 암호
→ 공개키 암호
RSA 등장 후 1년 후 디피, 헬먼이 공개키 암복호화 개념 소개 → 디피 헬먼은 공개키 서명 수행 안됨
소인수분해 이용하는 특성을 가지고 있음
디지털 서명 구축에 사용됨
개인키 소유자 → 서명 가능
공개키 이용 → 서명의 유효성 확인
트랩도어 치환 개념을 활용

트랩도어 치환

수 $x$를 같은 범위의 수 $y$로 변환하는 함수
공개키를 이용해서 $x$로부터 $y$를 계산하기 쉬움
하지만 개인키를 모르면 $y$로부터 $x$를 계산하는게 사실상 불가능한 방식으로 변환한다는 특징
→ RSA의 경우 $x$가 평문, $y$가 암호문


개념1

유클리드 알고리즘

$$ GCD(A,B)\;=\;GCD(B,r) $$
: 주어진 두 수 사이에 존재하는 최대공약수(GCD)를 구하는 알고리즘
두 수 : $a, b \;\;(a>b)$
몫 : $q$
나머지 : $r$
위를 정리해보면 $a = b*q+r$
유클리드 알고리즘은 위 식에서
$a = b*q+r$
$b = b1*r+r1$
$b1 = b2*r1+r2$

위와 같은 과정을 거쳐 나머지인 $r_n$이 0이 되었을 때 $b_n$의 값이 두 수의 최대공약수라는 것
위 식을 이용해서 두 수의 최대공약수를 구한다면 다음과 같음
두 수 : 100, 16
100 = 16*6+4
16 = 4*4+0
원래 우리가 일반적으로 하는 방식인 소인수분해한 다음 최대공약수를 구하는것과 비교해보면 훨씬 간단함
100 = 2*2*5*5
16 = 2*2*2*2
최대공약수 = 2*2 = 4
 

베주 항등식

확장 유클리드 알고리즘 → 베주 항등식의 명제를 가정
두 정수와 그 최대공약수 사이 관계 보여주는 항등식
3가지 참인 명제
$GCD(a, b) = d$ 라고 할 때

  1. $ax + by = d$ 를 만족하는 정수 $x, y$가 존재
  2. $d$는 정수 $x, y$에 대하여 $ax + by$ 로 표현할 수 있는 가장 작은 정수
  3. $ax + by$ 로 표현될 수 있는 모든 정수는 $d$의 배수

증명

$$ S\;=\;ax\;+\;by>0\;|\;x,y\in ℤ $$
집합 $S$는 자연수의 부분집합, $S$가 공집합이 아닐 경우
자연수 정렬성 → “공집합이 아닌 양의 정수의 모든 집합은 최소 원소를 갖는다.”
 

확장 유클리드 알고리즘

유클리드 알고리즘을 확장하여 $a, b$의 최대공약수 뿐만 아니라 $ax + by = gcd(a, b)$를 만족하는 정수해 $x, y$를 찾는 알고리즘
ex) $gcd(240, 46) = c$ 라고 할 때 $240x + 16y = c$의 해와 $c$ 구하기
최대공약수는 $2,\;$$2$$240x + 46y = 2$를 만족하는 정수해 $x = -9, y = 47$

용어 설명

$gcd(a,b)$ : 정수 $a, b$의 최대공약수
$a\equiv b\;(mod\;m)$ : $a$와 $b$는 $m$에 의해 나누어졌을 때의 나머지가 동일
이 수식이 성립하면 $a-b$는 $m$의 정수배라 볼 수 있음
이때 $a=b\;mod\;m$ 과 $a \equiv b\; (mod\;m)$은 다름
$=$ 이 들어가는거는 계산 결과를 나타냄
ex) 2 = 17 mod 5
$\equiv$ 가 들어가는건 표현한다는거, 같다는 의미를 나타냄 (합동)
ex) 17 = 2 mod 5


개념2

비대칭키 암호화 : 비밀키로 공개키 알아내기 o, 공개키로 비밀키 알아내기 x
→ 이산대수 어려움 통해 구현
RSA는 소인수분해의 난해함을 기초로 보안 유지
두 소수: $p, q$
$p*q$ = 쉬움 ($k$)
$k$ = 소인수분해 어려움 ($p$=?, $q$=?)
큰 수는 컴퓨터로도 소인수분해 어려움

페르마의 소정리

$$ p가 \;소수이면,\;모든\;정수\;a에\;대해\;a^p\equiv a\;(mod\;p)\\ p가\;소수이고\;a가\;p의\;배수가\;아니면,\;a^{p-1}\equiv1\,(mod\;p) $$
위의 두번째 경우라면 $p$는 소수가 아님
모든 합성수 $p$에 대해 성립 안됨, 확률적으로 $p$가 합성수인데 소수로 판별 가능
페르마 소정리는 많은 합성수들이 상대적으로 시간이 오래 걸리는 완전한 소수 판별 x, 합성수인지 알려줌
페르마 소정리 → 소수(소수일 가능성 정수) → 다른 완전한 방법으로 소수인지 확인 → $p, q$로 사용

오일러 정리

페르마의 소정리를 일반화한 것 → 소수 p를 정수 n으로 확장한 형태
$$ a와\;n이\;서로\;서로소인\;양의\;정수일\;때,\\a^{\phi(n)}\equiv1\;(mod\;n) $$


개념3

나머지 연산 매우 자주 활용

m = a%b

$a^b\equiv m\;(mod\;N)$에서 $m$을 계산하는 일을 매우 자주 수행
→ 큰 수의 나머지를 빠르고 간단하게 계산하는 방법

나머지의 성질

정리 1

$$ ab\;mod\;N=(a\;mod\;N)(b\;mod\;N)\,mod\;N $$
→ 두 수의 곱의 나머지는 각각의 수의 나머지를 먼저 구하고 그 나머지를 곱한 수의 나머지를 구해도됨

지수법칙

$b=p+q$일 때 $a^b=a^{p+q}=a^p*a^q$
——>
$$ a^b\;mod\;N=a^pa^q\;mod\;N=(a^p\;mod\;N)(a^q\;mod\;N)\,mod\;N $$

정리 2

$$ a^b\;mod\;N=(a\;mod\;N)^b\;mod\;N $$
나머지를 먼저 계산해서 밑을 작게 만들고 거듭제곱해서 연산 용량 줄이기


동작

기호

$N$ : 암복호화 과정에 이용되는 modulus로 사용, $N=p*q$ 인 합성수
$p, q$ : 공개되지 않은 두 소수
$e$ : 공개된 지수
$d$ : $d*e\equiv 1\;mod\;(p-1)(q-1)$을 만족하는 공개되지 않은 자연수
$m$ : 암호화 전 평문 (메세지), 이때 m < N
$c$ : 암호문
$E$ : 암호변환함수 (Encrypt)
$D$ : 복호변환함수 (Decrypt)
 

키 생성

  1. 큰 소수 $p, q$를 생성
  2. $N=pq$를 계산
  3. $(p-1)(q-1)$과 서로소인 정수 $e$를 정함
    1. $\phi(N)$는 오일러 피 함수 : $N$과 서로소인 $N$ 이하의 자연수 개수
      1. ex) 8 이하의 양의 정수 중 8과 서로소인 수는 1, 3, 5, 7로 4개임. $\phi(6)=2$
    2. $N$이 소수인 경우 $\phi(N)=N-1$ (1부터 $p-1$까지 모두 $p$와 서로소이기 때문에)
    3. 곱셈적 함수
    4. $\phi(N) = \phi(pq)=\phi(p)\phi(q)=(p-1)(q-1)$
  4. $ed$를 $(p-1)(q-1)$로 나눈 나머지가 1인 정수 $d$를 계산 (확장 유클리드 알고리즘 사용)
    1. $ed\equiv 1\;(mod\;\phi(N))$를 만족하는 정수 $d$
    2. $ed\;\% \;(p-1)(q-1)= 1$
    3. $d \equiv e^{-1}(mod\; \phi(n))$
  • 키로 사용되지 않는 두 소수 $p, q$는 공개되면 안됨
    • 공개열쇠 $e$로부터 개인열쇠 $d$를 찾을 수 있음
  • 상황에 따라 3단계와 4단계의 순서를 바꿔서 개인열쇠를 먼저 정하고 공개열쇠를 생성함

참고

3단계에서 $e, (p-1)(q-1)$이 서로소여야 하는 이유 - 4단계 (서로소가 아니면 위 조건을 만족하는 $d$는 존재하지 않음)
4단계는 어떤 정수 $x$가 있을 때 다음과 같이 요약 가능
$$ ed=(p-1)(q-1)x\,+\,1 $$
 

암호화

$$ c=m^e\;mod\;N $$
평문을 나타내는 자연수를 e제곱한 결과를 N으로 나눠준것
→ $e, N$을 알면 누구든 암호화 가능
암호화키쌍 : $(e, N)$
 

복호화

$$ m=c^d\;mod\;N $$
$d, N$을 알면 누구든 복호화 가능
복호화키쌍 : $(d, N)$


복호화 증명

$m=c^d\;mod\;N$
$=(m^e\;mod\;N)^d\;mod\;N$
$=m^{ed}\;mod\;N$
$\to m^{ed} \equiv m\;(mod\;N)$
$m^{ed} \equiv m\;(mod\;N)$를 증명하면됨
$N=p*q$ 이므로 위 합동식이 $mod\;p$일 때 또는 $mod\;q$일 때 성립하면 $mod\;N$일 때도 성립함
$ed\equiv1\;(mod\;\phi(N))$이므로 적당한 정수 $k$가 존재하여 $ed=k*\phi(N)+1=k(p-1)(q-1)+1$이라 쓸 수 있음

1. 페르마의 소정리 이용

위 합동식에 대입하면
$m^{ed}\equiv m\;(mod\;N)$
$\to m^{ed}\equiv m \;(mod\;p)$
$\to m^{k(p-1)(q-1)+1} \equiv m\;(mod\;p)$
$\to m^{k(p-1)(q-1)} \,*\,m\equiv m\;(mod\;p)$
$\to (m^{p-1})^{k(q-1)}\,*\,m \equiv m\;(mod\;p)$
 

경우 1 - m이 p의 배수가 아닐 때

$m$이 $p$의 배수가 아니면 $p$가 소수이기 때문에 페르마의 소정리에 의해
$m^{p-1}\equiv 1\;(mod\;p)$이고 이 식을 위에 대입하면
$(m^{p-1})^{k(q-1)}\,*\,m \equiv m\;(mod\;p)$
$\to 1^{k(q-1)}\,*\, m \equiv m\;(mod\;p)$
$\to m \equiv m\;(mod\;p)$
이므로 합동식이 성립함
 

경우 2 - m이 p의 배수일 때

$m$이 $p$의 배수면 합동식 양변이 모두 $p$로 나누어 떨어짐 → 0이됨
따라서 합동식이 성립함

2. 오일러 정리 이용

$m \equiv c^d \;(mod\;n) \\ c^d \equiv (m^e)^d \equiv m^{ed}\; (mod \; n)$
오일러 정리를 이용하면 $ed = x\phi(n)\;+\;1$을 만족하는 자연수 $x$가 존재함. 따라서 다음 합동식이 성립함
$m^{ed} \equiv (m^{\phi(n)})^km\;(mod\;n)$
$(m^{\phi(n)})^km \equiv\;m\;(mod\;n)$

그냥 전에 crypto 문제 이거저거 내보면서 각 crypto 분야별로 어떤식으로 해야할지 정리한거

고전암호

카이사르

$$ e_k(x)=x+k(mod\;26) \\ d_k(y)=y-k(mod\;26),\;\:x,\ y\in Z_{26} $$

  1. 평문 정하기
  2. 키 정하기 (1~26)
  3. 암호문 완성
#encrypt.py
word = 플래그 입력

def encrypt_caesar(text, key):
    result = ""
   # transverse the plain text
    for i in range(len(text)):
          char = text[i]
          # Encrypt uppercase characters in plain text
          if (char.isupper()):
             result += chr((ord(char) + key-65) % 26 + 65)
          # Encrypt lowercase characters in plain text
          else:
             result += chr((ord(char) + key - 97) % 26 + 97)
    return result

key = random.randrange(1, 26)
cipher = encrypt_caesar(word, key)

#decrypt.py
def caesar(word, shift):
    return ''.join([chr((ord(c) + shift - 97) % 26 + 97) for c in word])

for i in range(26):
        plaintext = caesar(w, i)
				print(plaintext)

곱셈암호

1. 키 설정

$$(k, 26) = 1이\;되는\;\;k \in Z_{26}\;값들은\;k = 1,3,5,7,9,11,15,17,19,21,23,25\;이다.$$

2. 암호화 함수 (평문 암호화)

$$ e_k(x)=(x \times k)\;mod\;26 $$

3. 복호화 함수 (암호문 복호화)

$$ d_k(y)=(y\times k^{-1})\;mod\;26 $$

#encrypt.py
def encrypt_multiplication(text, key):
    result = ""
   # transverse the plain text
    for i in range(len(text)):
          char = text[i]
          # Encrypt uppercase characters in plain text
          if (char.isupper()):
             result += chr((key*(ord(char) - 65)) % 26 + 65)
          # Encrypt lowercase characters in plain text
          else:
             result += chr((key*(ord(char) - 97)) % 26 + 97)
    return result

key_list = [3,5,7,9,11,15,17,19,21,23,25]
key = random.choice(key_list)
cipher = encrypt_multiplication(word, key)

#decrypt.py
def multiplication(w, key):
    p = ""
    calcdec = lambda c, a: (keyinverse(a, 26) * c) % 26
    for i in w :
        if i.isalpha() :
            if i in string.ascii_lowercase :
                p += string.ascii_lowercase[calcdec(string.ascii_lowercase.index(i), key)]
            else :
                p += string.ascii_uppercase[calcdec(string.ascii_uppercase.index(i), key)]
        else :
            p += i
    return p

for i in range(26):
        plaintext = multiplication(w, i)
				print(plaintext)

caesar, multiplication, affine, vigenere, playfair, hill 등 여러 고전암호 중 선택

그냥 암호문을 주고 풀면 플래그를 얻을 수 있는 방식, 암호문 여러개를 풀어야 플래그를 얻을 수 있는 방식, 코드를 올바르게 고쳐야 플래그를 얻을 수 있는 등 여러 방식으로 문제 출제

xor

xor 문제를 낼 때 보통 암호화만 시켜서 풀 수 있는건 비추 (다 툴 사용해서 풀어서)

그래서 아래처럼 xor이 완료된 암호문과 xor시킨 값을 찾을 수 있게 평문의 일부분을 제공함

import random
from itertools import cycle

MESSAGE = open("./original-message.txt", "r").read()
SECRET = [chr(random.randint(0,0x2600) % 256) for i in range(16)]

def encrypt(message):
    return [str((ord(a) ^ ord(b))) for a, b in zip(message, cycle(SECRET))]

with open('./message-encrypted.txt', 'w') as f:
    f.write(','.join(encrypt(MESSAGE)))

#interecepted_message
S*********s***y***k************w***s****************n***********************P***********_r*4*********************o***uck*****t******************

xor = [3804,6106,4562,8583,4559,3066,6690,9714,3901,7334,4777,7969,8139,3088,4719,2378,3834,6037,4501,8649,4585,2989,6753,9723,3900,7409,4858,7964,8161,3170,4662,2386,3808,6087,4501,8660,4520,3060,6753,9680,3900,7400,4797,7990,8143,3140,4707,2377,3822,6081,4503,8648,4584,3066,6779,9658,3955,7374,4799,7990,8139,3088,4735,2390,3759,6092,4497,8658,4596,3066,6695,9727,3890,7393,4832,8036,8190,3175,4696,2408,3786,6094,4559,8659,4569,2957,6773,9696,3852,7412,4841,8048,8143,3196,4719,2426,3781,6080,4557,8660,4529,2949,6656,9676,3883,7350,4744,7993,8078,3089,4662,2402,3808,6106,4506,8583,4586,2991,6690,9720,3955,7392,4789,7990,8078,3140,4734,2368,3759,6107,4507,8671,4594,3066,6690,9723,3890,7402,4790,7969,8128,3159,4723,2390]

위 코드같은 경우 xor시킨 값의 길이가 16이라 중간중간 주어진 알파벳은 xor 리스트를 여러번 반복해서 16글자가 모두 채워지도록 한 알파벳임

원래 secret 순서, 주어진 평문 문자열 순서, 문자, secret

1 0 S 143
2 113 o 181
3 18 k 254
4 35 s 167
5 52 n 134
6 117 u 218
7 118 c 65
8 119 c 147
9 88 _ 83
10 89 r 134
11 10 s 218
12 91 4 68
13 76 P 174
14 125 t 48
15 14 y 22
16 31 w 37

16개의 secret이 9번 반복

위와 같이 정리해서 풀 수 있음

  1. 평문 ^ xor_list = 암호문 중 암호문만 제공
    1. xor_list는 브포, 유추 가능하도록 쉬운걸로 설정하기
  2. 평문 ^ xor_list = 암호문 중 평문 일부와 암호문을 제공
    1. 평문 일부와 암호문을 이용해서 xor_list를 구할 수 있도록 평문 일부 제공하기
    2. 이때 xor_list로 암호화한 flag를 제공해줘서 xor_list를 구한 뒤 flag 암호문을 복호화할 수 있게

2022년도 중학생 교외대회 문제이지만 이제서야 올린..ㅎ
노션에 정리해서 옮기기 어려운 부분은 사진으로 넣음

catiplifine

flag: HSOC{Th1s_1s_Cl@ssical_C1pher}
사용기술: caesar cipher, affine cipher, multiplication cipher, pwntools, docker
출제의도: 고전암호인 카이사르 암호, 곱셈암호, 아핀암호의 복호화 코드를 짜고 pwntools를 사용할 수 있는가를 확인하기 위해 출제하였다.
 

문제

시나리오
선생님께서 10문제의 퀴즈를 본다고 5분 전에 말해주셨다.
분명히 고전암호 책이였는데… 그중에서 어떤 단원이였는지 기억이 안난다.
‘헉 벌써 시간이 3분밖에 안남았잖아,, 어떤단원인지 빨리 파악하고 퀴즈나 풀어야겠다 ㅠ’
prob

문제 설명

파이썬으로 random word list를 불러와 여러 고전암호를 암호화하여 출력한다.
접속해서 바로 출력되는 화면에는 고전암호 챌린지의 고전암호 목록(book index)과 랜덤 단어와 랜덤 키를 사용하고 있다고 힌트를 주고있다.

Option: g
g
Cipher: gfwlj
type a message: fjdhls
fjdhls
Noooo!

option G와 Q가 있는데 g를 입력하면 다음과 같이 랜덤한 단어에 랜덤한 암호의 랜덤한 키를 적용한 암호문이 나온다. 10초 이내로 풀지 못하면 Time is up! 이 출력되고 입력한 평문이 틀리다면 Noooo! 가 출력된다.
10초 동안 총 10문제를 풀면 플래그가 출력된다.
위의 3개의 암호방식이 사용되었다는 것을 알아도 복호화 툴이나 pwntools를 사용하지 않고 각각의 복호화 코드만 사용하여 문제를 푸는것을 방지하기 위해 시간을 10초로 설정해주었다.
참고로 caesar cipher, multiplication cipher, affine cipher 세개의 암호를 사용하였다는 점을 유추할 수 있도록 문제 제목을 catiplifine로 설정해주었다.

 

4. random word, docker 설정

random word는 https://random-word-api.herokuapp.com/all 에서 복사하여 wordlist를 만들어주었다.
Dockerfile

 

5. 문제 코드 작성 (암호화 코드)

catiplifine.py

 

6. 도커 실행

 

7. 문제 실행 및 확인

문제 확인시에 도커만 실행시켜서 접속하는 것이기 때문에 nc localhost 포트번호 형태로 한다.

❯ nc localhost 1366

    ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
    | Welcome to the classical cipher challenge!                           |
    | Find out what cipher has been used in <BOOK INDEX>                   |
    | * Random word, random key *                                          |
    |             ___________________     ___________________              |
    |         .-/|                    \ /                    |\-.          |
    |         ||||    <BOOK INDEX>     |                     ||||          |
    |         ||||                     |       ~~*~~         ||||          |
    |         |||| 1. Caesar Cipher    |                     ||||          |
    |         |||| 2. Multiplication   |                     ||||          |
    |         ||||    Cipher           |                     ||||          |
    |         |||| 3. Affine Cipher    |     --==*==--       ||||          |
    |         |||| 4. Vigenere Cipher  |                     ||||          |
    |         |||| 5. Playfair Cipher  |                     ||||          |
    |         |||| 6. Hill Cipher      |                     ||||          |
    |         |||| 7. Autokey Cipher   |                     ||||          |
    |         ||||__________________   | ____________________||||          |
    |         ||/=====================\|/=====================\||          |
    |         `----------------------~___~---------------------''          |
    |                                                                      |
    |----------------------------------------------------------------------|
    | For option                                                           |
    | Options:                                                             |
    |      [G]et the cipher                                                |
    |      [Q]uit                                                          |
    |                                                                      |
    
Option: g
g
Cipher: mzrlyd
type a message: Time is up!

제대로 실행되는 것을 확인할 수 있다.
 

풀이

1. 문제 파악

시나리오를 보면 book index에 있는 모든 고전암호 중 랜덤하게 골라서 하는게 아닌 그중 일부를 선택하여 암호화한 암호문을 복호화하여 10문제를 풀면 플래그를 출력해준다는 것을 유추할 수 있다.
book index의 여러 고전암호 중 어떤것일지는 문제 제목을 통해 유추할 수 있는데 catiplifine 이라는 제목을 보고 caesar cipher, multiplication cipher, affine cipher의 글자 중간중간을 따서 만들었다는 것을 알 수 있다.
10초 동안 10문제를 풀어야하기에 python과 pwntools를 사용해야하고 random word 리스트에서 동일한 단어가 나올때만 평문을 출력해야한다는 것을 알 수 있다. (최대 26가지 키를 각각 사용하는 암호방식 3가지가 있지만 입력할 수 있는 기회는 1번이기 때문이다)
 

2. 코드작성

pwntools와 각 암호의 복호화 코드를 이용하여 플래그를 출력해낼 수 있는 코드를 작성한다.
이때 wordlist는 random word api 라고 검색하여 리스트로 만들어 사용했다.


코드를 실행해보면 위와 같이 10초 내로 10문제를 풀어주고 마지막 문제를 풀면 플래그가 정상적으로 출력되는 것을 확인할 수 있다.

'HSOC 보안관제' 카테고리의 다른 글

2022 암호학 출제 문제 RSA (boneh durfee) writeup  (1) 2023.11.23
HSOC 암호학 멘토링 (rsa 기본원리)  (0) 2023.10.31
HSOC crypto 멘토링 2  (0) 2023.08.23
crypto 문제출제 정리  (0) 2023.07.11
HSOC crypto 멘토링 1  (0) 2023.04.05

동아리 멘토링 할 때도 본인이 멘토면 자료는 직접 만들어서 합시다
이거 혹은 다른 멘토 자료 그대로 본인 블로그에 올리거나 자료로 만들어서 쓰지 말고.. 안 그럴거라 믿을게요

여러 블로그랑 책 등을 참고해서 멘토링에 필요한 내용만 간단하게 정리한거임
문제를 만들거나 풀 때 사용되는 내용만 다룰 예정이고 용어의 정의나 뜻 같은거는 최대한 생략할 예정 (구글링하면 더 잘 설명해줌)
암호학을 공부하면 프로그래밍을 공부할 때 hello world를 치는것과 같이 비슷한게 있는데 보통 송신자(메세지 보내는사람)를 elice라고 부르고 수신자(메세지 받는사람)을 bob이라고 부름

 

암호학 기본 개념

암호의 특성

  • 기밀성(Confidentiality): 암호화된 내용이 무엇인지 알 수 없어야 함
  • 무결성(Integrity): 원본과 확실한 데이터라는 것
  • 인증(Authentication): 권한이 있는 사람만 접근할 수 있음

암복호화 과정

평문(plaintext): 원래 송신자가 전달하려는 원본 메세지
암호문(ciphertext): 송신자와 수신자만 전달 내용을 알 수 있도록 키를 사용해서 알아볼 수 없게 만든 메세지
암호화(encryption): 평문을 암호문으로 바꾸는것
복호화(decryption): 암호문을 평문으로 바꾸는것
키(key): 암복호화에 쓰이는 특정한 값 (자물쇠 열고 잠그는것처럼 키를 사용해서 암복호화를 진행하고 키를 모르면 복호화가 어려움)
 
당연하지만 해커가 중간에 메세지를 가로채서 이상한 메세지로 바꾸거나 메세지 내용을 알게되는것을 방지하기 위해 키는 공개하지 않음
하지만 암호화 알고리즘, 키 길이는 대부분 공개함

 

암복호화 표기법

  • 증명 과정이나 암호 알고리즘 설명에 쓰이는 정수론 표기법 같은거는 나중에 설명할 예정

암호 알고리즘이나 원리를 설명할 때 항상 암호화, 복호화, 키, 평문 등 이런식으로 표기하기 번거롭기 때문에 다음과 같이 표기함
$Z_n$ : n이 양의 정수일 때 0부터 n-1까지의 정수 집합임
ex) $Z_{26}$은 0~25까지의 정수 집합으로 총 26가지임. 보통 알파벳 집합을 나타낼 때 이와같이 표기함
$P$ : plaintext
$C$ : ciphertext
$k$ : key
$e_k$ : encrypt key - 암호화할 때 사용하는 키
$d_k$ : decrypt key - 복호화할 때 사용하는 키
 
위 표기법을 이용하면 다음과 같이 수식을 작성할 수 있음
$$ C = e_k(P) \\ P = d_k(C) $$
 

고전암호

치환 암호 (Substitution Cipher)

: 평문의 문자를 다른 문자로 바꾸어서 암호화하는 방식
 

1. 단일 치환 암호

: 평문의 각 문자를 약속된 다른 문자로 바꾸는거
 

1-1. 카이사르 암호 (Caesar cipher)

<암호화>
26개의 알파벳을 나열해서 시프트해주어 암호화를 진행함
예를 들어 a~z 알파벳 배열을 3칸씩 시프트해주면 다음과 같이 됨
그럼 a-d, b-e, c-f, d-g, e-h, f-i 등과 같이 일대일 대응이 됨
$P = C=K=Z_{26}$이라 하면 이때 $0\leqq k \leqq 25$ 에 대하여 암호화 함수는 다음과 같음
 
$$ e_k(x) = x+k\;(mod\;26) $$

 

<복호화>
복호화 할때는 반대로 다시 3칸을 뒤로 밀어주고 평문을 계산하면 됨
복호화 함수는 다음과 같고 x, y도 다음과 같이 정의할 수 있음

 

$$ d_k(y)=y-k\;(mod\;26),\\x, \ y\in Z_{26} $$

 

위 두개의 암호화 함수, 복호화 함수를 통해 $d_k(e_k(x))=d_k(x+k)=x+k-k=x$ 임을 알 수 있음

 
1-2. 곱셈 암호 (Multiplication Cipher)

<키 설정>
(k, 26) = 1이 되는 $k \in Z_{26}$ 값들은 k = 1,3,5,7,9,11,15,17,19,21,23,25 

 

<암호화>
$$ e_k(x)=(x \times k)\;mod\;26 $$

 

아까 카이사르 암호는 덧셈을 사용했지만 여기서는 곱셈을 해준뒤에 26으로 나머지 연산을 해줌
예를 들어 키가 7이라고 하면 다음과 같이 할 수 있음

 
 

<복호화>
$$ d_k(y)=(y\times k^{-1})\;mod\;26 $$

 
1-3. 아핀 암호 (Affine Cipher)

암호화로 따졌을 때 카이사르는 덧셈, 곱셈암호는 곱셈이였는데 아핀암호는 그 두개를 결합한 형태

 

$$ P=C=Z_{26} \\ K= \left\{ (a,b)\in Z_{26} \times Z_{26} \mid (a,\;26)=1 \right\} \\ k=(a,b) \in K,\;x,y \in Z_{26} $$

 

<키 설정>
26 = 2*13이기 때문에 (a, 26) = 1이 되는 $a \in Z_{26}$ 값들은 a = 1,3,5,7,9,11,15,17,19,21,23,25 임
하지만 b는 $Z_{26}$의 어느 값을 가져도 되기 때문에 아핀암호에서 가능한 키 수는 12*26=312개임

 

<암호화>
$$ e_k(x)=ax+b\;(mod\;26) $$
암호화할 때 곱셈에 쓰일 키, 덧셈에 쓰일 키 2개를 지정해야함
예를 들어 k = (7, 3)이면 암호함수는 $e_k(x)=7x+3\;(mod\;26)$임

 

<복호화>
$$ d_k(y)=a^{-1}(y-b)(mod\;26) $$
아까 예시 그대로 k = (7, 3)으로 지정했을 때 복호함수 $d_k(y)$를 구하기 위해서는 먼저 $7^{-1}$을 구해야함
$7 \times 15 = 105 \equiv 1\;(mod\;26)$이므로  $7^{-1} \equiv 15\;(mod\;26)$임
따라서 $d_k(y)=15y+7\;(mod\;26)$

 

2. 다중 문자 치환 암호

: 평문의 한 문자가 암호문에서 여러 종류의 문자로 바뀔 수 있음
 

2-1. 비즈네르 암호

<암호화>
$$ e_k(x_i)=(x_i+k_i)\;mod26 $$
전에 카이사르 암호에서 키가 12였으면 a를 12칸, b를 12칸, c를 12칸 이런식으로 시프트했었음
근데 비즈네르에서는 키가 12일 때 a는 1칸, b는 2칸, c는 1칸, d는 2칸 이런식으로 키 길이를 평문 길이만큼 반복해서 돌림
그래서 다음 사진과 같은 비즈네르 스퀘어를 이용함
 

 

HELLO를 CUP으로 암호화시킨다고 하면
H,C가 만나는 J
E,U가 만나는 Y 이런식으로 할 수 있음

 

<복호화>

 

$$ d_k(y_i)=(y_i-k_i)\;mod26 $$

 

암호화할 때랑 반대로 실행하면 됨
보통은 kasiski test 라는 공격법을 사용하는데 vigenere cipher decode라고 검색하면 복호화 사이트 많이 나옴

 

전치 암호 (Transposition Cipher)

: 전에 치환암호는 a → d, b → e 이런식으로 다른 문자로 대체했었다면 전치 암호는 문자의 순서를 규칙에 따라 무작위로 바꿈
치환암호랑 다르게 암호화함수, 복호화함수 이렇게 정의해서 표현하기가 애매함 (나중에 시간있을 때 책 찾아보거나 해서 있으면 정리해볼 예정)
https://ieatt.tistory.com/27
이 블로그 보고 이런 종류가 있고 이런식으로 하는거구나 정도만 알아두기

 

현대암호

대칭키, 비대칭키, 단방향 이런거는 다음 멘토링때

 

그 외

xor

http://m.blog.naver.com/oidoman/221145674383

base64

https://dokhakdubini.tistory.com/505

 

RSA, AES

RSA

  • 유클리드 호제법 (확장 유클리드도)
  • 베주 항등식
  • 페르마 소정리
  • 오일러 정리

AES

  • DES
  • 행렬
  • 갈루아 필드
  • 체 이론
  • 군 이론

chall.py

#!/usr/bin/python3
from Crypto.Util.number import getPrime, long_to_bytes, inverse
flag = open('flag.txt', 'r').read().strip().encode()

class RSA:
    def __init__(self):
        self.p = getPrime(512)
        self.q = getPrime(512)
        self.e = 3
        self.n = self.p * self.q
        self.d = inverse(self.e, (self.p-1)*(self.q-1))
    def encrypt(self, data: bytes) -> bytes:
        pt = int(data.hex(), 16)
        ct = pow(pt, self.e, self.n)
        return long_to_bytes(ct)
    def decrypt(self, data: bytes) -> bytes:
        ct = int(data.hex(), 16)
        pt = pow(ct, self.d, self.n)
        return long_to_bytes(pt)

def main():
    crypto = RSA()
    print ('Flag:', crypto.encrypt(flag).hex())

if __name__ == '__main__':
    main()

 

output.txt

Flag: 05c61636499a82088bf4388203a93e67bf046f8c49f62857681ec9aaaa40b4772933e0abc83e938c84ff8e67e5ad85bd6eca167585b0cc03eb1333b1b1462d9d7c25f44e53bcb568f0f05219c0147f7dc3cbad45dec2f34f03bcadcbba866dd0c566035c8122d68255ada7d18954ad604965

 

주어진 두 코드를 보면 ct와 e가 주어진 RSA 문제라는 것을 알 수 있다

n 또한 주어지지 않았기 때문에 ct와 매우 작은 e를 사용하여 낮은지수 공격을 수행하면 될 것 같다

 

sol.py

from gmpy2 import *
from Crypto.Util.number import long_to_bytes

c = int('05c61636499a82088bf4388203a93e67bf046f8c49f62857681ec9aaaa40b4772933e0abc83e938c84ff8e67e5ad85bd6eca167585b0cc03eb1333b1b1462d9d7c25f44e53bcb568f0f05219c0147f7dc3cbad45dec2f34f03bcadcbba866dd0c566035c8122d68255ada7d18954ad604965', 16)
e = 3
 
with local_context() as ctx:
    ctx.precision=3000
    m=iroot(c,e)[0]
 
    print(long_to_bytes(m))

 

'wargame' 카테고리의 다른 글

HTB RSAisEasy writeup  (0) 2023.03.30
HTB xorxorxor writeup  (0) 2023.03.30
PwnPwn wargame noisy writeup  (0) 2022.10.30
HTB BabyEncryption writeup  (0) 2022.10.23
old-01 풀이  (0) 2022.07.27

chall.py

#!/usr/bin/env python3
from Crypto.Util.number import bytes_to_long, getPrime
from secrets import flag1, flag2
from os import urandom

flag1 = bytes_to_long(flag1)
flag2 = bytes_to_long(flag2)

p, q, z = [getPrime(512) for i in range(3)]

e = 0x10001

n1 = p * q
n2 = q * z

c1 = pow(flag1, e, n1)
c2 = pow(flag2, e, n2)  

E = bytes_to_long(urandom(69))

print(f'n1: {n1}')
print(f'c1: {c1}')
print(f'c2: {c2}')
print(f'(n1 * E) + n2: {n1 * E + n2}')

 

output.txt

n1: 101302608234750530215072272904674037076286246679691423280860345380727387460347553585319149306846617895151397345134725469568034944362725840889803514170441153452816738520513986621545456486260186057658467757935510362350710672577390455772286945685838373154626020209228183673388592030449624410459900543470481715269
c1: 92506893588979548794790672542461288412902813248116064711808481112865246689691740816363092933206841082369015763989265012104504500670878633324061404374817814507356553697459987468562146726510492528932139036063681327547916073034377647100888763559498314765496171327071015998871821569774481702484239056959316014064
c2: 46096854429474193473315622000700040188659289972305530955007054362815555622172000229584906225161285873027049199121215251038480738839915061587734141659589689176363962259066462128434796823277974789556411556028716349578708536050061871052948425521408788256153194537438422533790942307426802114531079426322801866673
(n1 * E) + n2: 601613204734044874510382122719388369424704454445440856955212747733856646787417730534645761871794607755794569926160226856377491672497901427125762773794612714954548970049734347216746397532291215057264241745928752782099454036635249993278807842576939476615587990343335792606509594080976599605315657632227121700808996847129758656266941422227113386647519604149159248887809688029519252391934671647670787874483702292498358573950359909165677642135389614863992438265717898239252246163

 

주어진 코드와 값을 보면 n1을 이용해서 p와 q를 구해볼 수 있다

factordb에 n1을 돌려보면 다음과 같이 p, q가 나온다

(이때 소수 두개가 나오는데 이 중에서 뭐가 p이고 q인지 알 수 없음 -> flag1을 구할 때는 p,q가 바뀌어도 딱히 상관없음)

p = 12040644312371555810530782070969893153760288255333349208608058511112776958879208815174991008199408527954332776642365069284747758115478414463195873149420483
q = 8413387656561188778435613942028835678781206299389177514340760123063579360223360470566083306606450007991287094526418200038784207648097820069671213638771543

이렇게 p, q값을 구했으니 flag1은 구할 수 있지만 n2를 아직 모르기 때문에 flag2는 구할 수 없다

output.txt의 마지막 값인 (n1 * E) + n2를 이용해서 n2를 구해볼 것인데 n1*E+n2에서 n2를 구하려면 (n1*E+n2)%n1 을 하면된다

n1_E_n2 = 601613204734044874510382122719388369424704454445440856955212747733856646787417730534645761871794607755794569926160226856377491672497901427125762773794612714954548970049734347216746397532291215057264241745928752782099454036635249993278807842576939476615587990343335792606509594080976599605315657632227121700808996847129758656266941422227113386647519604149159248887809688029519252391934671647670787874483702292498358573950359909165677642135389614863992438265717898239252246163
n1 = 101302608234750530215072272904674037076286246679691423280860345380727387460347553585319149306846617895151397345134725469568034944362725840889803514170441153452816738520513986621545456486260186057658467757935510362350710672577390455772286945685838373154626020209228183673388592030449624410459900543470481715269

n2 = n1_E_n2%n1
n2 = 100136903041423020991425823526737746365573197640035952973693624809721624428963253203282593974533722584391447008912397042291986993273828302711324440847902763039627790146764630023926517236880457533976468679976683705170312329736955922713306570804595070537102421450884645497775455984735279182873866159334387494837

그리고 이제 z의 값도 구할 수 있다

(처음 문제를 풀 때는 p, q의 값을 바꿔서 써서 n2 = n1_E_n2 % n1의 값과 n2//q로 구한 z의 값을 이용한 n2 = q*z의 값이 달랐었다)

z값은 n2//q를 이용해서 구한다

 

그럼 필요한 값은 모두 구했기 때문에 flag1과 flag2를 구해서 전체 플래그를 구할 수 있다

 

sol.py

from Crypto.Util.number import inverse, long_to_bytes

n1 = 101302608234750530215072272904674037076286246679691423280860345380727387460347553585319149306846617895151397345134725469568034944362725840889803514170441153452816738520513986621545456486260186057658467757935510362350710672577390455772286945685838373154626020209228183673388592030449624410459900543470481715269
c1 = 92506893588979548794790672542461288412902813248116064711808481112865246689691740816363092933206841082369015763989265012104504500670878633324061404374817814507356553697459987468562146726510492528932139036063681327547916073034377647100888763559498314765496171327071015998871821569774481702484239056959316014064
c2 = 46096854429474193473315622000700040188659289972305530955007054362815555622172000229584906225161285873027049199121215251038480738839915061587734141659589689176363962259066462128434796823277974789556411556028716349578708536050061871052948425521408788256153194537438422533790942307426802114531079426322801866673
n1_e_n2 = 601613204734044874510382122719388369424704454445440856955212747733856646787417730534645761871794607755794569926160226856377491672497901427125762773794612714954548970049734347216746397532291215057264241745928752782099454036635249993278807842576939476615587990343335792606509594080976599605315657632227121700808996847129758656266941422227113386647519604149159248887809688029519252391934671647670787874483702292498358573950359909165677642135389614863992438265717898239252246163
e = 0x10001

p = 12040644312371555810530782070969893153760288255333349208608058511112776958879208815174991008199408527954332776642365069284747758115478414463195873149420483
q = 8413387656561188778435613942028835678781206299389177514340760123063579360223360470566083306606450007991287094526418200038784207648097820069671213638771543

n2 = n1_e_n2%n1
z = n2//q

phi1 = (p-1) * (q-1)
d1 = inverse(e, phi1)

phi2 = (q-1) * (z-1)
d2 = inverse(e, phi2)

flag1 = pow(c1, d1, n1)
flag2 = pow(c2, d2, n2)

print(long_to_bytes(flag1) + long_to_bytes(flag2))

'wargame' 카테고리의 다른 글

HTB Lost Modulus writeup  (0) 2023.03.30
HTB xorxorxor writeup  (0) 2023.03.30
PwnPwn wargame noisy writeup  (0) 2022.10.30
HTB BabyEncryption writeup  (0) 2022.10.23
old-01 풀이  (0) 2022.07.27

chall.py

#!/usr/bin/python3
import os
flag = open('flag.txt', 'r').read().strip().encode()

class XOR:
    def __init__(self):
        self.key = os.urandom(4)
    def encrypt(self, data: bytes) -> bytes:
        xored = b''
        for i in range(len(data)):
            xored += bytes([data[i] ^ self.key[i % len(self.key)]])
        return xored
    def decrypt(self, data: bytes) -> bytes:
        return self.encrypt(data)

def main():
    global flag
    crypto = XOR()
    print ('Flag:', crypto.encrypt(flag).hex())

if __name__ == '__main__':
    main()

 

output.txt

Flag: 134af6e1297bc4a96f6a87fe046684e8047084ee046d84c5282dd7ef292dc9

 

밑의 chall.py 코드를 살펴보면

xored += bytes([data[i] ^ self.key[i % len(self.key)]])

len(self.key)는 os.urandom(4)로 작성했기 때문에 4로 항상 동일하다

따라서 self.key[i % len(self.key)]] 는 i가 4보다 작으면 i값 그대로 리턴해주고 4보다 크면 i-4를 리턴해준다

 

이때 self.key[]는 self.key[0], self.key[1], self.key[2], self.key[3] 이렇게 4개만 있다

-> 그래서 플래그 길이만큼 10진수 4개가 반복해서 플래그 인덱스 i와 xor되어서 이를 바이트로 차례대로 저장한다

 

그리고 이를 16진수로 바꾼게 제공받은 output.txt이다

 

플래그 중 일부분인 HTB{ 라는 플래그 형식을 알고있고 4글자라는 것을 이용해 self.key를 찾아낼 수 있다

output으로 제공받은 16진수에서 2글자 당 평문 글자 하나를 암호화한다

이를 이용해서 키를 구하는 코드를 작성해보면 다음과 같다

a = ['H', 'T', 'B', '{']
b = ['13', '4a', 'f6', 'e1']

key = []
for i in range(4):
    a[i] = hex(ord(a[i]))[2:]
    key.append(int(hex(int(a[i],16)^int(b[i], 16))[2:], 16))

print(key)

그럼 키는 [91, 30, 180, 154] 라는 것을 알 수 있다

 

이제 이 키를 이용해서 제공받은 16진수 플래그를 2글자씩 끊어서 xor해주면 평문인 플래그가 나올 것이다

 

sol.py

#a = ['H', 'T', 'B', '{']
#b = ['13', '4a', 'f6', 'e1']
#
#key = []
#for i in range(4):
#    a[i] = hex(ord(a[i]))[2:]
#    key.append(int(hex(int(a[i],16)^int(b[i], 16))[2:], 16))
#
#print(key)

key = [91, 30, 180, 154]

flag = '134af6e1297bc4a96f6a87fe046684e8047084ee046d84c5282dd7ef292dc9'

arr = []

for i in range(0, len(flag), 2):
    arr.append(int(flag[i] + flag[i+1], 16))

msg = b""
for i in range(len(arr)):
    msg += bytes([arr[i] ^ key[i % 4]])

print(msg)

 

'wargame' 카테고리의 다른 글

HTB Lost Modulus writeup  (0) 2023.03.30
HTB RSAisEasy writeup  (0) 2023.03.30
PwnPwn wargame noisy writeup  (0) 2022.10.30
HTB BabyEncryption writeup  (0) 2022.10.23
old-01 풀이  (0) 2022.07.27

Crypto

Just a XOR

import random
from itertools import cycle

MESSAGE = "S"
SECRET = [chr(random.randint(0,0x2600) % 256) for i in range(16)]

#0~97344까지의 랜덤한 숫자를 16번 반복해서 SECRET에 넣는다. (문자열로 변환)

def encrypt(message):
    return [str((ord(a) ^ ord(b))) for a, b in zip(message, cycle(SECRET))]

#print(','.join(encrypt(MESSAGE)))

secret과 문자열을 xor해서 반환함

랜덤한 secret list를 찾아내야함

따라서 intercepted-original-message.txt에 있는 문자열과 주어진 xor된 숫자를 비교하며 secret list를 찾아내야함

(주어진 평문 문자도 딱 16개임)

원래 secret 순서, 주어진 평문 문자열 순서, 문자, secret

1 0 S 143
2 113 o 181
3 18 k 254
4 35 s 167
5 52 n 134
6 117 u 218
7 118 c 65
8 119 c 147
9 88 _ 83
10 89 r 134
11 10 s 218
12 91 4 68
13 76 P 174
14 125 t 48
15 14 y 22
16 31 w 37

16개의 secret이 9번 반복

secret을 구하고 그냥 바로 xor해주는게 아니고 순서가 섞여있기 때문에 제대로 순서를 배치해주고 평문을 구해야함

아까 코드로 구했던 secret 처음값을 순서대로 다시 만들면 다음과 같음

[143, 181, 254, 167, 134, 218, 65, 147, 83, 134, 218, 68, 174, 48, 22, 37]

이제 제대로된 SECRET을 사용해서 주어진 enc와 xor해보면 평문이 나옴

from itertools import cycle

SECRET = [143, 181, 254, 167, 134, 218, 65, 147, 83, 134, 218, 68, 174, 48, 22, 37]
msg = [3804,6106,4562,8583,4559,3066,6690,9714,3901,7334,4777,7969,8139,3088,4719,2378,3834,6037,4501,8649,4585,2989,6753,9723,3900,7409,4858,7964,8161,3170,4662,2386,3808,6087,4501,8660,4520,3060,6753,9680,3900,7400,4797,7990,8143,3140,4707,2377,3822,6081,4503,8648,4584,3066,6779,9658,3955,7374,4799,7990,8139,3088,4735,2390,3759,6092,4497,8658,4596,3066,6695,9727,3890,7393,4832,8036,8190,3175,4696,2408,3786,6094,4559,8659,4569,2957,6773,9696,3852,7412,4841,8048,8143,3196,4719,2426,3781,6080,4557,8660,4529,2949,6656,9676,3883,7350,4744,7993,8078,3089,4662,2402,3808,6106,4506,8583,4586,2991,6690,9720,3955,7392,4789,7990,8078,3140,4734,2368,3759,6107,4507,8671,4594,3066,6690,9723,3890,7402,4790,7969,8128,3159,4723,2390]


def decrypt(message):
    return [((a ^ b) % 256) for a, b in zip(message, cycle(SECRET))]

msg = decrypt(msg)

string = ''.join([chr(num) for num in msg])

print(string)

# So, I can see you know how XOR works.. Congratulation :) Here is your flag: PWNME{1t_W4s_r34aLy_Ju3s7_A_x0R} ! Good luck for the next challenges

PWNME{1t_W4s_r34aLy_Ju3s7_A_x0R}

 

Osint

Social Media Goes Brrrrr

트위터나 인스타그램에 찾아도 안나와서 페북에 john droper를 찾아봄

https://www.facebook.com/profile.php?id=100091409530073&sk=about_details

해당 프로필의 자세한 소개에 들어갔더니 플래그에 대한 언급이 있었음

PWNME{TG9uZyBsaXZlIHRoZSB0cmFpbnMsIGxvbmcgbGl2}

'ctf writeup' 카테고리의 다른 글

2023 APOLLO writeup  (1) 2023.10.31
27회 해킹캠프 CTF writeup  (0) 2023.09.09
escape ctf 2023 writeup  (0) 2023.02.13
WaniCTF 2023 writeup  (0) 2023.02.13
Incognito 4.0 (ictf 2023)  (0) 2023.02.12

Crypto

Bunker Escape

prob.py

from Crypto.Util.number import bytes_to_long, long_to_bytes, getPrime
from gmpy2 import invert

class Cipher:
    def __init__(self):
        self.n = 108506951736793336490683880256855846248083684741694466461336182348417411176781023957825388818844036495751633876599810798436954790114984279939172259886462851438954959031979074506295441495658302003723943179142742062326225122087241430684094279948641138924448463919864585525265270367948313098530841624367001646231
        self.e = 65537

    def encrypt(self, msg):
        return pow(msg, self.e, self.n)

ments = [
    ?, ?, ?, ?, ?, ...
        ]

RSA = Cipher()

while True:
    for ment in ments:
        c = RSA.encrypt(bytes_to_long(ment))
        print(c)

c가 주어지지 않음

두개의 서버가 있는데 첫번째 서버에서는 암호를 입력해야 플래그를 준다고함

두번째 서버에 접속하면 ments 몇개를 계속 반복해서 출력해줌

c.txt

2036295046017029554592558827250389978504009297171635141995321017850956408156217880176445091794538831577452207188372751785425107738901243282510519480235140348861474251724258318445744420728905224779862162281842654717946873831355210852371307510186377109810663851312252715534383547988198506921895221049544022590
37575027881690023592896789531214751324339071798634201243381144078885909309871776459999707639661538809832816523762731336144814773076184762362745510604029738395053773214814099775158166400114070313639131167227705575975092463727540812106587708699677459499462142123816906054855831047820524243826514049131473606896
44638378662797495237588537527696380419060598329695652920169549474299659869405531490922828159027027657701652812711728829903955539284493737370969098515494779713775178033180473988734519938002974831487915719240424268540426017998416587095934878282282899961063954724881617070207962219334963337726915641645376075740
39572521168934688860500761800131891754675815943387093665718065175399710012231012765149908900075517093669336575002712617801689823107954250371529815094072074222208829041696135343433771032595958399003751718117411139391824832520480069478328890918965842244849986096662572359121814154200629412476674249851245181204

위와 같은 암호문을 복호화하면 암호를 구할 수 있음

sol.py

from Crypto.Util.number import long_to_bytes

c = [2036295046017029554592558827250389978504009297171635141995321017850956408156217880176445091794538831577452207188372751785425107738901243282510519480235140348861474251724258318445744420728905224779862162281842654717946873831355210852371307510186377109810663851312252715534383547988198506921895221049544022590, 37575027881690023592896789531214751324339071798634201243381144078885909309871776459999707639661538809832816523762731336144814773076184762362745510604029738395053773214814099775158166400114070313639131167227705575975092463727540812106587708699677459499462142123816906054855831047820524243826514049131473606896, 44638378662797495237588537527696380419060598329695652920169549474299659869405531490922828159027027657701652812711728829903955539284493737370969098515494779713775178033180473988734519938002974831487915719240424268540426017998416587095934878282282899961063954724881617070207962219334963337726915641645376075740, 39572521168934688860500761800131891754675815943387093665718065175399710012231012765149908900075517093669336575002712617801689823107954250371529815094072074222208829041696135343433771032595958399003751718117411139391824832520480069478328890918965842244849986096662572359121814154200629412476674249851245181204]

for i in range(len(c)):
    n = 108506951736793336490683880256855846248083684741694466461336182348417411176781023957825388818844036495751633876599810798436954790114984279939172259886462851438954959031979074506295441495658302003723943179142742062326225122087241430684094279948641138924448463919864585525265270367948313098530841624367001646231
    e = 65537
    # n이 소수임
    # n이 소수이므로 phi(n) = n - 1
    # e가 소수이므로 e는 phi(n)과 서로소
    # 따라서 d = e^-1 mod phi(n) = e^-1 mod (n - 1)

    phi = n - 1
    d = pow(e, -1, phi)
    m = pow(c[i], d, n)
    print(long_to_bytes(m))

# b'The enemy broke into the bunker.'
# b'Lock all the doors of the bunker.'
# b'All soldiers search for intruders.'
# b'Door Password is T$s15d#'
nc 34.87.17.26 10001
T$s15d#





input door code : ESCAPE{Escape_enemy_bunker!!_you_live!}
ESCAPE{Escape_enemy_bunker!!_you_live!}
 

Escaping Blowfish

prob.py

from Crypto.Cipher import Blowfish
from Crypto.Util.Padding import pad

key = b"N0bOdy_cAn_FiNd_Th1s!"  

plaintext = b"ESCAPE{x_xxxx_xxxxxxxxxxxx_xxx_xxxx_xx_xxx_xxxxx_xxx}"

cipher = Blowfish.new(key, Blowfish.MODE_ECB)

padded_plaintext = pad(plaintext, 8)

ciphertext = cipher.encrypt(padded_plaintext)

print(ciphertext)

서버에 접속하면 암호문을 얻을 수 있음

nc 34.64.33.48 20001
b'\x0b\xfc\x8d\xcb\x0eE\x9cdh\x8br8\x14\xb0HV\xd6M\xb2\xda\x88L\xe3"\xeb\xb6\xafS\xd4[\xb1S\x93\xda\xcdF;\x19\xb9\xc5x\xe2D\xad\x88g\xb5\x82\xa4\xe3X\xeb\x02\x15\xdf\xee'
sol.py

from Crypto.Cipher import Blowfish
from Crypto.Util.Padding import unpad

key = b"N0bOdy_cAn_FiNd_Th1s!"
ciphertext = b"\x0b\xfc\x8d\xcb\x0eE\x9cdh\x8br8\x14\xb0HV\xd6M\xb2\xda\x88L\xe3\"\xeb\xb6\xafS\xd4[\xb1S\x93\xda\xcdF;\x19\xb9\xc5x\xe2D\xad\x88g\xb5\x82\xa4\xe3X\xeb\x02\x15\xdf\xee"

cipher = Blowfish.new(key, Blowfish.MODE_ECB)

decrypted_padded = cipher.decrypt(ciphertext)
decrypted_plaintext = unpad(decrypted_padded, 8)

print(decrypted_plaintext.decode('utf-8'))

ESCAPE{I_l0v3_CrYpt0grAph7_th3_mOst_1n_th3_w0r1d_!!!}

 

miracle file

encrypt.py

from OpenSSL.crypto import load_publickey, FILETYPE_PEM
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes

def encrypt_with_public_key(public_key_path, input_file_path, output_file_path):
    with open(public_key_path, 'rb') as key_file:
        public_key_data = key_file.read()
        public_key = serialization.load_pem_public_key(public_key_data)

    with open(input_file_path, 'rb') as input_file:
        input_data = input_file.read()

    encrypted_data = public_key.encrypt(
        input_data,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )

    with open(output_file_path, 'wb') as output_file:
        output_file.write(encrypted_data)

public_key_path = 'public_key.pem'
input_file_path = 'secret.txt'
output_file_path = 'out.enc'

encrypt_with_public_key(public_key_path, input_file_path, output_file_path)

out.enc, private_key.pem도 주어짐

sol.py

from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding

private_key_path = 'private_key.pem'
encrypted_data_path = 'out.enc'

with open(private_key_path, 'rb') as key_file:
    private_key_data = key_file.read()
    private_key = serialization.load_pem_private_key(private_key_data, password=None)

with open(encrypted_data_path, 'rb') as encrypted_file:
    encrypted_data = encrypted_file.read()

decrypted_data = private_key.decrypt(
    encrypted_data,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

print(decrypted_data.decode('utf-8'))

ESCAPE{3C370603784112F75B70A9E2BCF1764C}

 

misc

escape support

사진에 나와있는 목표지점의 위도와 경도를 플래그로 작성하면 됨

구글 지도로 해당 건물이 있는 위치를 클릭해서 위도와 경도를 확인하고 소수 2째자리까지 자름

ESCAPE{38.59_128.36}

 

'ctf writeup' 카테고리의 다른 글

27회 해킹캠프 CTF writeup  (0) 2023.09.09
Pwnme qual 2023 writeup  (0) 2023.02.26
WaniCTF 2023 writeup  (0) 2023.02.13
Incognito 4.0 (ictf 2023)  (0) 2023.02.12
Knight CTF 2023 writeup  (0) 2023.02.12

Crypto

EZDORSA_Lv1

p = 3
q = 5
n = p*q
e = 65535
c ≡ m^e (mod n) ≡ 10 (mod n)

#sol.py
p = 3
q = 5
n = p * q
e = 65535
# c = m**e mod n = 10 mod n
c = pow(10, e, n)

phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
m = pow(c, d, n)
print(m)

FLAG{THE_ANSWER_IS_10}

 

EZDORSA_Lv2

c = pow(bytes_to_long(m), e, n)
c *= pow(5, 100, n)

e가 굉장히 작기 때문에 원래 c값을 구한 후에 낮은지수 공격을 수행하면됨

#sol1.py
from Crypto.Util.number import long_to_bytes
import gmpy2

n = 25465155563758206895066841861765043433123515683929678836771513150236561026403556218533356199716126886534636140138011492220383199259698843686404371838391552265338889731646514381163372557117810929108511770402714925176885202763093259342499269455170147345039944516036024012941454077732406677284099700251496952610206410882558915139338028865987662513205888226312662854651278789627761068396974718364971326708407660719074895819282719926846208152543027213930660768288888225218585766787196064375064791353928495547610416240104448796600658154887110324794829898687050358437213471256328628898047810990674288648843902560125175884381
e = 7
c = 25698620825203955726406636922651025698352297732240406264195352419509234001004314759538513429877629840120788601561708588875481322614217122171252931383755532418804613411060596533561164202974971066750469395973334342059753025595923003869173026000225212644208274792300263293810627008900461621613776905408937385021630685411263655118479604274100095236252655616342234938221521847275384288728127863512191256713582669212904042760962348375314008470370142418921777238693948675063438713550567626953125

c //= pow(5, 100, n)

for i in range(100000):
    crack = gmpy2.iroot(c + n*i, e)[0]
    crack = long_to_bytes(crack)
    if b"FLAG{" in crack:
        print("crack [{}] = {}".format(i, crack))

브포 사용함

#sol2.py
from Crypto.Util.number import long_to_bytes
from gmpy2 import *

n = 25465155563758206895066841861765043433123515683929678836771513150236561026403556218533356199716126886534636140138011492220383199259698843686404371838391552265338889731646514381163372557117810929108511770402714925176885202763093259342499269455170147345039944516036024012941454077732406677284099700251496952610206410882558915139338028865987662513205888226312662854651278789627761068396974718364971326708407660719074895819282719926846208152543027213930660768288888225218585766787196064375064791353928495547610416240104448796600658154887110324794829898687050358437213471256328628898047810990674288648843902560125175884381
e = 7
c = 25698620825203955726406636922651025698352297732240406264195352419509234001004314759538513429877629840120788601561708588875481322614217122171252931383755532418804613411060596533561164202974971066750469395973334342059753025595923003869173026000225212644208274792300263293810627008900461621613776905408937385021630685411263655118479604274100095236252655616342234938221521847275384288728127863512191256713582669212904042760962348375314008470370142418921777238693948675063438713550567626953125

c //= pow(5, 100, n)

with local_context() as ctx:
    ctx.precision=3000
    m = iroot(c, e)[0]

    print(long_to_bytes(m))

FLAG{l0w_3xp0n3nt_4ttAck}

 

EZDORSA_Lv3

n = 1
prime_list = []
while len(prime_list) < 100:
    p = getPrime(25)
    if not (p in prime_list):
        prime_list.append(p)

for i in prime_list:
    n *= i

100이하 길이의 p 리스트를 계속 추가하여 n에 계속 곱해줌

주어진 n값을 factordb에 돌려보면 p list가 나올 것 같음

이를 txt파일로 작성해서 리스트로 불러옴

from Crypto.Util.number import long_to_bytes

p = []
#sol.txt 내용 가져오기
with open("sol.txt", "r") as f:
    for i in range(100):
        p.append(int(f.readline()))

n = 22853745492099501680331664851090320356693194409008912025285744113835548896248217185831291330674631560895489397035632880512495471869393924928607517703027867997952256338572057344701745432226462452353867866296639971341288543996228186264749237402695216818617849365772782382922244491233481888238637900175603398017437566222189935795252157020184127789181937056800379848056404436489263973129205961926308919968863129747209990332443435222720181603813970833927388815341855668346125633604430285047377051152115484994149044131179539756676817864797135547696579371951953180363238381472700874666975466580602256195404619923451450273257882787750175913048168063212919624027302498230648845775927955852432398205465850252125246910345918941770675939776107116419037
e = 65537
c = 1357660325421905236173040941411359338802736250800006453031581109522066541737601274287649030380468751950238635436299480021037135774086215029644430055129816920963535754048879496768378328297643616038615858752932646595502076461279037451286883763676521826626519164192498162380913887982222099942381717597401448235443261041226997589294010823575492744373719750855298498634721551685392041038543683791451582869246173665336693939707987213605159100603271763053357945861234455083292258819529224561475560233877987367901524658639475366193596173475396592940122909195266605662802525380504108772561699333131036953048249731269239187358174358868432968163122096583278089556057323541680931742580937874598712243278738519121974022211539212142588629508573342020495

phi = 1
for i in p:
    phi *= (i-1)

d = pow(e, -1, phi)
m = pow(c, d, n)
print(long_to_bytes(m))

FLAG{fact0r1z4t10n_c4n_b3_d0n3_3as1ly}

 

pqqp

s = (pow(p, q, n) + pow(q, p, n)) % n

(pow(p, q, n) + pow(q, p, n)) = n*a + s
  • pow(p, q, n) = pow(p, q, p*q) = pow(p, q, p) * pow(p, q, q)
  • pow(q, p, n) = pow(q, p, p*q) = pow(q, p, p) * pow(q, p, q) 이므로,
  • s = (pow(p, q, p) * pow(p, q, q) + pow(q, p, p) * pow(q, p, q)) % n

또한, 페르마의 소정리에 의해, 소수 p와 정수 a에 대하여 a^p ≡ a (mod p) 이 성립함

이를 이용하면 다음과 같이 정리할 수 있음

  • pow(a, p, p) = a (mod p)
  • pow(a, q, q) = a (mod q)

따라서,

  • s ≡ (p + q) (mod n) 가 성립함. 이 결과를 이용하면 s와 n 값이 주어졌을 때 p와 q 값을 찾을 수는 없지만, p+q 값을 구할 수 있음
n = 31091873146151684702346697466440613735531637654275447575291598179592628060572504006592135492973043411815280891993199034777719870850799089897168085047048378272819058803065113379019008507510986769455940142811531136852870338791250795366205893855348781371512284111378891370478371411301254489215000780458922500687478483283322613251724695102723186321742517119591901360757969517310504966575430365399690954997486594218980759733095291730584373437650522970915694757258900454543353223174171853107240771137143529755378972874283257666907453865488035224546093536708315002894545985583989999371144395769770808331516837626499129978673  # n 값
s = 352657755607663100038622776859029499529417617019439696287530095700910959137402713559381875825340037254723667371717152486958935653311880986170756144651263966436545612682410692937049160751729509952242950101025748701560375826993882594934424780117827552101647884709187711590428804826054603956840883672204048820926   # s 값

p_plus_q = s % n
print(p_plus_q)

#p+q = 352657755607663100038622776859029499529417617019439696287530095700910959137402713559381875825340037254723667371717152486958935653311880986170756144651263966436545612682410692937049160751729509952242950101025748701560375826993882594934424780117827552101647884709187711590428804826054603956840883672204048820926

p + q = p_plus_q
p * q = n
p^2 + p_plus_q * p - n = 0
p = (-p_plus_q + sqrt(p_plus_q^2 + 4n)) / 2

 

위 식은 근의 공식을 이용하여 p와 q의 값을 구하는 방법 중 하나임

우선, p + q = p_plus_q, p * q = n 이므로, p와 q의 두 개의 식을 만들 수 있음
p = n / q

q = n / p
여기서 p와 q를 더하면, p + q = n / q + n / p
이 식에서, 분모를 pq로 공통 분모로 만들면, p + q = (p^2 + q^2) / pq
여기서, p * q = n 이므로, p + q = (p^2 + q^2) / n
이를 n으로 곱하면, p * n + q * n = p^2 + q^2
여기서, p + q = p_plus_q 이므로, q = p_plus_q - p
이 식을 위의 식에 대입하면, p * n + (p_plus_q - p) * n = p^2 + (p_plus_q - p)^2
2 * p * p_plus_q - p^2 - (p_plus_q)^2 = 0
p^2 - 2 * p * p_plus_q + (p_plus_q)^2 = 4 * n
위 식에서 근의 공식을 이용하여 p를 구하면,
p = (-p_plus_q + sqrt(p_plus_q^2 + 4n)) / 2
따라서, 위의 식이 성립함

p = (-p_plus_q + gmpy2.isqrt(p_plus_q**2 + 4*n)) // 2
q = (-p_plus_q - gmpy2.isqrt(p_plus_q**2 + 4*n)) // 2

print(p)
print(q)

#p = 73037812621554340918423144065930814812766434975537849902945417309196851639257451196817067117965537928062548936597706502340256467815326129795643879578863303705661035602968433823314908774132944056315312602365678117543482970174353502507131619430715497449869327808111560185134257973346711934285117792716099706936
#q = 425695568229217440957045920924960314342184051994977546190475513010107810776660164756198942943305575182786216308314858989299192121127207115966400024230127270142206648285379126760364069525862454008558262703391426819103858797168236097441556399548543049551517212517299271775563062799401315891126001464920148527863
#q = 425695568229217440957045920924960314342184051994977546190475513010107810776660164756198942943305575182786216308314858989299192121127207115966400024230127270142206648285379126760364069525862454008558262703391426819103858797168236097441556399548543049551517212517299271775563062799401315891126001464920148527866

q가 p보다 한자리수 더 많고 q값이 확실하지 않음

이는 주어진 수 n의 소인수 분해를 할 때, 더 작은 소수를 먼저 찾은 다음에 나누어 떨어지는 큰 소수를 찾는 방식으로 진행하기 때문

따라서, 만약 작은 소수인 p가 큰 소수인 q보다 더 크게 나오는 경우, 다음과 같이 코드를 수정하여 더 작은 소수를 q로 할당하면됨

if p > q:
    p, q = q, p
import gmpy2

n = 31091873146151684702346697466440613735531637654275447575291598179592628060572504006592135492973043411815280891993199034777719870850799089897168085047048378272819058803065113379019008507510986769455940142811531136852870338791250795366205893855348781371512284111378891370478371411301254489215000780458922500687478483283322613251724695102723186321742517119591901360757969517310504966575430365399690954997486594218980759733095291730584373437650522970915694757258900454543353223174171853107240771137143529755378972874283257666907453865488035224546093536708315002894545985583989999371144395769770808331516837626499129978673  # n 값
s = 352657755607663100038622776859029499529417617019439696287530095700910959137402713559381875825340037254723667371717152486958935653311880986170756144651263966436545612682410692937049160751729509952242950101025748701560375826993882594934424780117827552101647884709187711590428804826054603956840883672204048820926   # s 값

p_plus_q = gmpy2.iroot(gmpy2.mpz(s)**2 - 4*gmpy2.mpz(n), 2)[0]
p = (-p_plus_q + gmpy2.isqrt(p_plus_q**2 + 4*n)) // 2
q = (-p_plus_q - gmpy2.isqrt(p_plus_q**2 + 4*n)) // 2

if p > q:
    p, q = q, p

print("p = ", p)
print("q = ", q)

#p =  -176330063921012922700637991462863541103998854114502331637244568587102359549595630463000846482379642953757505012490743127493606379742898143117619312114492521189590382203087267098186155670651109512140181571865807227830860711167013696000707228430366770561270525802688909225479987434756271082423940874678428380699
#q =  176327691686650177337984785396165958425418762904937364650285527113808599587807083096381029342960394300966162359226409359465329273568982843053136832536771445246955230479323425838863005081078400440102768529159941473729515115826868898933717551687460781540377358906498802364948817391298332874416942797525620440227

하지만 이때 p*q == n이 성립하지 않음

그래도 p, q값이 비슷하고 자릿수도 같아서 그냥 이 값으로 한번 계산해보기로 함

from Crypto.Util.number import long_to_bytes, inverse

n = 31091873146151684702346697466440613735531637654275447575291598179592628060572504006592135492973043411815280891993199034777719870850799089897168085047048378272819058803065113379019008507510986769455940142811531136852870338791250795366205893855348781371512284111378891370478371411301254489215000780458922500687478483283322613251724695102723186321742517119591901360757969517310504966575430365399690954997486594218980759733095291730584373437650522970915694757258900454543353223174171853107240771137143529755378972874283257666907453865488035224546093536708315002894545985583989999371144395769770808331516837626499129978673
e = 65537
c = 8684906481438508573968896111659984335865272165432265041057101157430256966786557751789191602935468100847192376663008622284826181320172683198164506759845864516469802014329598451852239038384416618987741292207766327548154266633297700915040296215377667970132408099403332011754465837054374292852328207923589678536677872566937644721634580238023851454550310188983635594839900790613037364784226067124711011860626624755116537552485825032787844602819348195953433376940798931002512240466327027245293290482539610349984475078766298749218537656506613924572126356742596543967759702604297374075452829941316449560673537151923549844071

p = 176330063921012922700637991462863541103998854114502331637244568587102359549595630463000846482379642953757505012490743127493606379742898143117619312114492521189590382203087267098186155670651109512140181571865807227830860711167013696000707228430366770561270525802688909225479987434756271082423940874678428380699
q = 176327691686650177337984785396165958425418762904937364650285527113808599587807083096381029342960394300966162359226409359465329273568982843053136832536771445246955230479323425838863005081078400440102768529159941473729515115826868898933717551687460781540377358906498802364948817391298332874416942797525620440227

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))

FLAG{p_q_p_q_521d0bd0c28300f}

 

Forensic

Just_mp4

주어진 mp4 파일을 hxd로 열어보면 마지막에 플래그가 나와있음

 

f.l.a.g._.b.a.s.e.6.4.:.R.k.x.B.R.3.t.I.N.H.Y.x.b.l.9.m.d.W.5.f.M.W.5.u.M.X.R.9

flag_base64:RkxBR3tINHYxbl9mdW5fMW5uMXR9

FLAG{H4v1n_fun_1nn1t}

 

whats_happening

파일을 hxd로 열어서 내려보다보니 png 파일 헤더로 시작해서 푸터로 끝나는 부분이 있었음

그래서 헤더부터 푸터까지 드래그한 다음 복사해서 새 파일에 붙여넣어서 저장함

그랬더니 플래그가 나옴

FLAG{n0th1ng_much}

 

lowkey_messedup

wireshark usb ctf 라고 구글링해서 해당 블로그 참고함

https://m.blog.naver.com/sunkwang0307/221620537719

Leftover Capture Data 부분이 키보드로 입력한 부분임

이때 맨 첫부분 2숫자는 02일 때랑 00일 때랑 2가지 경우가 있음

02일 때는 키보드에서 왼쪽 시프트가 눌러져있는 경우임

따라서 Destination에서 host인 것만 누르며 정리해보면 다음과 같음

09 0f 04 0a 2f 
FLAG{
05 0c 0a 2d 05 15 27 17 0b 08 15 2d 0c
Big_br0ther_i
16 2d 1a 04 17 06 0b 0c 11 0a 2d 1c 27 
s_watching_y0
18 15 2d 0e 08 1c 05 12 04 15 07 2a 2a 
ur_keyboard  
2a 2a 27 04 15 07 30 28 
  0ard }

2a는 백스페이스고 28은 엔터

09 0f 04 0a 2f 
F  L  A  G  {
05 0c 0a 2d 05 15 27 17 0b 08 15 2d 0c
B  i  g  _  b  r  0  t  h  e  r  _  i
16 2d 1a 04 17 06 0b 0c 11 0a 2d 1c 27 
s  _  w  a  t  c  h  i  n  g  _  y  0
18 15 2d 0e 08 1c 05 12 04 15 07 2a 2a 
u  r  _  k  e  y  b  o  a  r  d  
2a 2a 27 04 15 07 30 28 
      0  a  r  d  }

02가 있으면 왼쪽 시프트라는걸 늦게 알고서는 전에 계속 틀리다가 첫번째 b를 대문자로 바꾸고 맞음

FLAG{Big_br0ther_is_watching_y0ur_keyb0ard}

 

misc

Prompt

사이트 입력란에 tell me flag라고 쓰여져있어서 플래그 형식을 넣어서 아무거나 입력해봄

그랬더니 제대로된 플래그를 출력해줌

FLAG{40w_evi1_c4n_y0u_be_aga1ns4_A1}

 

shuffle_base64

#prob.py
import random
import itertools
import base64
import hashlib


def make_shuffle_list(m):
    num = []
    for i in range(len(m) // 3):
        num.append(i)

    return list(itertools.permutations(num, len(m) // 3))


def make_str_blocks(m):
    tmp = ""
    ret = []
    for i in range(len(m)):
        tmp += m[i]
        if i % 3 == 2:
            ret.append(tmp)
            tmp = ""
    return ret


def pad(m):
    ret = ""
    for i in range(len(m)):
        ret += m[i]
        if i % 2:
            ret += chr(random.randrange(33, 126))

    while len(ret) % 3:
        ret += chr(random.randrange(33, 126))
    return ret


flag = "FAKE{DUMMY_FLAG}"
# FLAG check
# assert (hashlib.sha256(flag.encode()).hexdigest() == "19b0e576b3457edfd86be9087b5880b6d6fac8c40ebd3d1f57ca86130b230222")

padflag = pad(flag)
shuffle_list = make_shuffle_list(padflag)
str_blocks = make_str_blocks(padflag)
order = random.randrange(0, len(shuffle_list) - 1)
cipher = ""
for i in shuffle_list[order]:
    cipher += str_blocks[i]
cipher = base64.b64encode(cipher.encode())

print(f"cipher = {cipher}")

암호화 과정

  1. 주어진 문자열을 padding
  2. 문자열을 3개씩 잘라 block들을 만듬
  3. block들의 순서를 무작위로 섞음
  4. 섞인 block들을 다시 이어붙여 하나의 문자열을 만듬
  5. 이어붙인 문자열을 base64 인코딩하여 암호화된 문자열(cipher)을 만듬
}d(leqFLTff|64,hu!{s@AG!

}d(leqFLTff|64,hu!{s@AG!
규칙적으로 랜덤한 ascii 문자가 붙고 그 ascii문자를 제거한 뒤 순서만 잘 맞추면 됨

}d(
leq
FLT
ff|
64,
hu!
{s@
AG!
이런식으로 뒤에 ascii를 하나씩 붙였다고 생각하고 뒤에 ascii를 제거한 뒤 순서를 맞춤

FLAG{shuffle64}

FLAG{shuffle64}

 

web

IndexedDB

문제 시나리오를 보면 개발자 도구를 사용해서 보라고함

개발자 도구에서 source에 플래그가 없어서 제목과 같은 indexedDB를 살펴봄

FLAG{y0u_c4n_u3e_db_1n_br0wser}

 

Pwnable

netcat

if (chall == 0) {
      printf("Bye!\n");
      break;
    }
    printf("%3d + %3d = ", x, y);
    scanf("%7s", buf);
    if (atoi(buf) == x + y) {
      printf("Cool!\n");
      score++;
    } else {
      printf("Oops...\n");
      score = 0;
    }
    if (score >= 3) {
      printf("Congrats!\n");
      win();
    }

    x = rand_gen();
    y = rand_gen();
    chall--;
  }
  return 0;
}

netcat 접속화면과 코드를 보면 한번 연산이 맞을 때마다 점수가 1이 추가되고 틀리면 1이 마이너스되고 3 이상이면 시스템 권한을 얻는것 같음

ls로 파일을 확인하고 cat으로 파일을 열었음

FLAG{1375_k339_17_u9_4nd_m0v3_0n_2_7h3_n3x7!}

'ctf writeup' 카테고리의 다른 글

Pwnme qual 2023 writeup  (0) 2023.02.26
escape ctf 2023 writeup  (0) 2023.02.13
Incognito 4.0 (ictf 2023)  (0) 2023.02.12
Knight CTF 2023 writeup  (0) 2023.02.12
KalmarCTF 2023 writeup  (0) 2023.02.12

525팀 중 289등 ㅎ..

sanity

flag: ictf{heres_s0m3_s4n1ty_91823dd}

 

more sanity

flag: ictf{!flag_work5??_p718jq091}

 

Crypto

Ancient

challenge.png를 열어봤을 때 IHDR, IDAT, IEND 다 있는데 헤더 시그니처만 안보여서 앞에 헤더 시그니처를 추가해줌

그런데도 이미지가 열리지 않아서 더 살펴봤는데 IHDR이 엉뚱한 곳에 위치해 있었음

원래 png 이미지 구조상 10~13까지가 가로, 14~17까지가 세로인 것으로 알고 있는데 세로 자리에 IHDR이 위치해 있어서 안열리는 것 같았음

가로와 똑같이 세로를 복사해서 이전 IHDR 자리에 붙여넣어줬더니 이미지가 열림

아래와 같은 이미지가 나왔는데 어떤 cipher인지 모르겠어서 이미지 검색을 해봄

monk cipher였음

https://jsom1.github.io/_challenges/templed

문양에 해당하는 문자가 있는 숫자들을 모두 더하고 10진수를 순서대로 나열해서 문자열로 바꾸는 것임

dcode.fr 이라는 사이트를 통해 다음과 같이 쉽게 숫자를 계산할 수 있었음

105 99 116 102 123 48 108 100 95 109 48

110 107 95 49 57 48 100 101 49 99 51 125

숫자는 위와 같은데 이를 문자열로 바꾸면 다음과 같음

flag: ictf{0ld_m0nk_190de1c3}

 

Crypto1

#sol2.py

import string
import io

#res = open('result', "rb").read().encode('utf8')

res = io.open('result', "r", encoding='utf-8').read()

def func(f, i):
    if i<5:
        out = ord(f) ^ 0x76 ^ 0xAD
        var1 = (out & 0xAA) >> 1
        var2 = 2 * out & 0xAA
        return var1 | var2 
    elif i>=5 and i<10:
        out = ord(f) ^ 0x76 ^ 0xBE
        var1 = (out & 0xCC) >> 2
        var2 = 4 * out & 0xCC
        return var1 | var2
    else:
        out = ord(f) ^ 0x76 ^ 0xEF
        var1 = (out & 0xF0) >> 4
        var2 = 16 * out & 0xF0
        return var1 | var2

flag = []

while len(flag) < 15:
    i = len(flag)
    for h in string.printable:
        if ord(res[i]) == func(h, i):
            flag.append(h)
            

print(flag)
print("".join(flag))
#sol3.py

import io

flag = "" #redacted

def func(f, i):
    if i<5:
        out = ord(f) ^ 0x76 ^ 0xAD
        var1 = (out & 0xAA) >> 1
        var2 = 2 * out & 0xAA
        return var1 | var2 
    elif i>=5 and i<10:
        out = ord(f) ^ 0x76 ^ 0xBE
        var1 = (out & 0xCC) >> 2
        var2 = 4 * out & 0xCC
        return var1 | var2
    else:
        out = ord(f) ^ 0x76 ^ 0xEF
        var1 = (out & 0xF0) >> 4
        var2 = 16 * out & 0xF0
        return var1 | var2

res = io.open('result', "r", encoding='utf-8').read()

for letter in res:
    for i in range(0, 256):
        if func(chr(i), res.index(letter)) == ord(letter):
            flag += chr(i)
            break

print(flag)

flag: ictf{88f30d1cd1ab443}

'ctf writeup' 카테고리의 다른 글

escape ctf 2023 writeup  (0) 2023.02.13
WaniCTF 2023 writeup  (0) 2023.02.13
Knight CTF 2023 writeup  (0) 2023.02.12
KalmarCTF 2023 writeup  (0) 2023.02.12
HSOC 교내 해킹방어대회 100점 문제 writeup  (0) 2022.10.09