튜기's blogggg

SSCTF HeHeDa Writeup

by St1tch

def LShift(t, k):
    k %= 8
    return ((t << k) | (t >> (8 - k))) & 0xff


def encode(p):
    ret = ""
    for i in range(8):
        ret = ('|' if (p >> i) & 1 else 'O') + ret
    return ret


A = [85, 128, 177, 163, 7, 242, 231, 69, 185, 1, 91, 89, 80, 156, 81, 9, 102, 221, 195, 33, 31, 131, 179, 246, 15, 139, 205, 49, 107, 193, 5, 63, 117, 74, 140, 29, 135, 43, 197, 212, 0, 189, 218, 190, 112, 83, 238, 47, 194, 68, 233, 67, 122, 138, 53, 14, 35, 76, 79, 162, 145, 51, 90, 234, 50, 6, 225, 250, 215, 133, 180, 97, 141, 96, 20, 226, 3, 191, 187, 57, 168, 171, 105, 113, 196, 71, 239, 200, 254, 175, 164, 203, 61, 16, 241, 40, 176, 59, 70, 169, 146, 247, 232, 152, 165, 62, 253, 166, 167, 182, 160, 125, 78, 28, 130, 159, 255, 124, 153, 56, 58, 143, 150, 111, 207, 206, 32, 144,
     75, 39, 10, 201, 204, 77, 104, 65, 219, 98, 210, 173, 249, 13, 12, 103, 101, 21, 115, 48, 157, 147, 11, 99, 227, 45, 202, 158, 213, 100, 244, 54, 17, 161, 123, 92, 181, 243, 184, 188, 84, 95, 27, 72, 106, 192, 52, 44, 55, 129, 208, 109, 26, 24, 223, 64, 114, 19, 198, 23, 82, 120, 142, 178, 214, 186, 116, 94, 222, 86, 251, 36, 4, 248, 132, 25, 211, 199, 30, 87, 60, 127, 155, 41, 224, 151, 237, 136, 245, 37, 170, 252, 8, 42, 209, 46, 108, 88, 183, 149, 110, 66, 235, 229, 134, 73, 38, 118, 236, 119, 154, 216, 217, 240, 22, 121, 174, 93, 126, 230, 228, 18, 148, 220, 172, 2, 137, 34]
B = [0, 2, 3, 7, 1, 5, 6, 4]
C = [179, 132, 74, 60, 94, 252, 166, 242, 208, 217, 117, 255, 20, 99, 225, 58, 54, 184, 243, 37, 96, 106, 64, 151, 148, 248, 44, 175, 152, 40, 171, 251, 210, 118, 56, 6, 138, 77, 45, 169, 209, 232, 68, 182, 91, 203, 9, 16, 172, 95, 154, 90, 164, 161, 231, 11, 21, 3, 97, 70, 34, 86, 124, 114, 119, 223, 123, 167, 47, 219, 197, 221, 193, 192, 126, 78, 39, 233, 4, 120, 33, 131, 145, 183, 143, 31, 76, 121, 92, 153, 85, 100, 52, 109, 159, 112, 71, 62, 8, 244, 116, 245, 240, 215, 111, 134, 199, 214, 196, 213, 180, 189, 224, 101, 202, 201, 168, 32, 250, 59, 43, 27, 198, 239, 137, 238, 50,
     149, 107, 247, 7, 220, 246, 204, 127, 83, 146, 147, 48, 17, 67, 23, 93, 115, 41, 191, 2, 227, 87, 173, 108, 82, 205, 49, 1, 66, 105, 176, 22, 236, 29, 170, 110, 18, 28, 185, 235, 61, 88, 13, 165, 188, 177, 230, 130, 253, 150, 211, 42, 129, 125, 141, 19, 190, 133, 53, 84, 140, 135, 10, 241, 222, 73, 12, 155, 57, 237, 181, 36, 72, 174, 207, 98, 5, 229, 254, 156, 178, 128, 55, 14, 69, 30, 194, 122, 46, 136, 160, 206, 26, 102, 218, 103, 139, 195, 0, 144, 186, 249, 79, 81, 75, 212, 234, 158, 163, 80, 226, 65, 200, 38, 187, 113, 63, 24, 25, 142, 51, 228, 35, 157, 216, 104, 162, 15, 89]
D = [2, 4, 0, 5, 6, 7, 1, 3]

plain = bytearray("asdfghjk123456")
key = bytearray(/*Missed*/)
assert len(key) == 8
t1 = bytearray()
for i in plain:
    t1.append(A[i])
t2 = bytearray()
for i in range(len(t1)):
    t2.append(LShift(t1[i], B[i % 8]))
for times in range(16):
    for i in range(len(t2)):
        t2[i] = C[t2[i]]
    for i in range(len(t2)):
        t2[i] = LShift(t2[i], i ^ D[i % 8])
    for i in range(len(t2)):
        t2[i] ^= key[i % 8]
out = ""
for i in t2:
    out += encode(i)
print out

# out>>
# OO|OO||OO|||||OO|OO||O||O|O||O|||O|OOOOOOO|O|O|O|||||OO|||O|||OO||O|OOOOOO|O|OO|OO||||OO|||OOOO|||||O||||O|OO|O|O|O||OO|O||O|OO|O||O|||O||O|OO|OOOOOO||OOO|O|O|O|||O|OO|O|O||O||O||OOOOO|||OO|O|

# flag >>
# OO||O||O|O|||OOOO||||||O|O|||OOO||O|OOOO||O|O|OO|||||OOOO||||O||OO|OO||O||O|O|O|||||OOOOOO|O|O||OOOOOOO||O|||OOOO||OO|OO|||O|OO|O|||O|O|OO|OOOO|OOO|OOO|OOOO||O|OO||||OO||||OOO|O|O||OO||||O||OOO|||O|OO|OO||OO||OOOO|O|



문제를 보면 암호화된 문자열 출력이 두가지가 있다.

plain = bytearray("asdfghjk123456") 일 경우 한가지와,  plain = flag일때 경우 한가지이다.


flag를 알기 위해서는 plain = flag 일 때의 암호화된 문자열을 key를 이용하여 복호화 하여 알아내야 하는 것을 알 수 있다.



A = [85, 128, 177, 163, 7, 242, 231, 69, 185, 1, 91, 89, 80, 156, 81, 9, 102, 221, 195, 33, 31, 131, 179, 246, 15, 139, 205, 49, 107, 193, 5, 63, 117, 74, 140, 29, 135, 43, 197, 212, 0, 189, 218, 190, 112, 83, 238, 47, 194, 68, 233, 67, 122, 138, 53, 14, 35, 76, 79, 162, 145, 51, 90, 234, 50, 6, 225, 250, 215, 133, 180, 97, 141, 96, 20, 226, 3, 191, 187, 57, 168, 171, 105, 113, 196, 71, 239, 200, 254, 175, 164, 203, 61, 16, 241, 40, 176, 59, 70, 169, 146, 247, 232, 152, 165, 62, 253, 166, 167, 182, 160, 125, 78, 28, 130, 159, 255, 124, 153, 56, 58, 143, 150, 111, 207, 206, 32, 144,
	 75, 39, 10, 201, 204, 77, 104, 65, 219, 98, 210, 173, 249, 13, 12, 103, 101, 21, 115, 48, 157, 147, 11, 99, 227, 45, 202, 158, 213, 100, 244, 54, 17, 161, 123, 92, 181, 243, 184, 188, 84, 95, 27, 72, 106, 192, 52, 44, 55, 129, 208, 109, 26, 24, 223, 64, 114, 19, 198, 23, 82, 120, 142, 178, 214, 186, 116, 94, 222, 86, 251, 36, 4, 248, 132, 25, 211, 199, 30, 87, 60, 127, 155, 41, 224, 151, 237, 136, 245, 37, 170, 252, 8, 42, 209, 46, 108, 88, 183, 149, 110, 66, 235, 229, 134, 73, 38, 118, 236, 119, 154, 216, 217, 240, 22, 121, 174, 93, 126, 230, 228, 18, 148, 220, 172, 2, 137, 34]
B = [0, 2, 3, 7, 1, 5, 6, 4]
C = [179, 132, 74, 60, 94, 252, 166, 242, 208, 217, 117, 255, 20, 99, 225, 58, 54, 184, 243, 37, 96, 106, 64, 151, 148, 248, 44, 175, 152, 40, 171, 251, 210, 118, 56, 6, 138, 77, 45, 169, 209, 232, 68, 182, 91, 203, 9, 16, 172, 95, 154, 90, 164, 161, 231, 11, 21, 3, 97, 70, 34, 86, 124, 114, 119, 223, 123, 167, 47, 219, 197, 221, 193, 192, 126, 78, 39, 233, 4, 120, 33, 131, 145, 183, 143, 31, 76, 121, 92, 153, 85, 100, 52, 109, 159, 112, 71, 62, 8, 244, 116, 245, 240, 215, 111, 134, 199, 214, 196, 213, 180, 189, 224, 101, 202, 201, 168, 32, 250, 59, 43, 27, 198, 239, 137, 238, 50,
	 149, 107, 247, 7, 220, 246, 204, 127, 83, 146, 147, 48, 17, 67, 23, 93, 115, 41, 191, 2, 227, 87, 173, 108, 82, 205, 49, 1, 66, 105, 176, 22, 236, 29, 170, 110, 18, 28, 185, 235, 61, 88, 13, 165, 188, 177, 230, 130, 253, 150, 211, 42, 129, 125, 141, 19, 190, 133, 53, 84, 140, 135, 10, 241, 222, 73, 12, 155, 57, 237, 181, 36, 72, 174, 207, 98, 5, 229, 254, 156, 178, 128, 55, 14, 69, 30, 194, 122, 46, 136, 160, 206, 26, 102, 218, 103, 139, 195, 0, 144, 186, 249, 79, 81, 75, 212, 234, 158, 163, 80, 226, 65, 200, 38, 187, 113, 63, 24, 25, 142, 51, 228, 35, 157, 216, 104, 162, 15, 89]
D = [2, 4, 0, 5, 6, 7, 1, 3]

def LShift(t, k):
	k %= 8
	return ((t << k) | (t >> (8 - k))) & 0xff

def encode(p):
	ret = ""
	for i in range(8):
		ret = ('|' if (p >> i) & 1 else 'O') + ret
	return ret

def calculate_out(plaintext, key) :
	t1 = bytearray()
	for i in plaintext:
		t1.append(A[i])
	t2 = bytearray()
	for i in range(len(t1)):
		t2.append(LShift(t1[i], B[i % 8]))
	for times in range(16):
		for i in range(len(t2)):
			t2[i] = C[t2[i]]
		for i in range(len(t2)):
			t2[i] = LShift(t2[i], i ^ D[i % 8])
		for i in range(len(t2)):
			t2[i] ^= key[i % 8]
	out = ""
	for i in t2:
		out += encode(i)
	return out

def findkey(plaintext, encrypted) :
	keylist = [ [] for i in range(8) ]
	for time in range(8):
		origin_key = "0" * time
		for k in range(0x0, 0x100) :
			tmp = origin_key + ( chr(k) * (8 - len(origin_key)) )
			key = bytearray(tmp)
			out = calculate_out(plaintext, key)
			index = 8 * len(origin_key)
			if(encrypted[index:index+8] == out[index:index+8]) :
				keylist[len(origin_key)].append(chr(k))
	return keylist

#calculate key
plain = bytearray("asdfghjk123456")
out = "OO|OO||OO|||||OO|OO||O||O|O||O|||O|OOOOOOO|O|O|O|||||OO|||O|||OO||O|OOOOOO|O|OO|OO||||OO|||OOOO|||||O||||O|OO|O|O|O||OO|O||O|OO|O||O|||O||O|OO|OOOOOO||OOO|O|O|O|||O|OO|O|O||O||O||OOOOO|||OO|O|"
keylist = findkey(plain, out)
print '\n'.join( [ "key[%d] = "%i + str(keylist[i]) for i in range(len(keylist)) ] ) + '\n\n'

#find flag
import string
maps = string.ascii_letters + "1234567890-=!@#$%^&*()_+,.[]{}"
enc_flag = "OO||O||O|O|||OOOO||||||O|O|||OOO||O|OOOO||O|O|OO|||||OOOO||||O||OO|OO||O||O|O|O|||||OOOOOO|O|O||OOOOOOO||O|||OOOO||OO|OO|||O|OO|O|||O|O|OO|OOOO|OOO|OOO|OOOO||O|OO||||OO||||OOO|O|O||OO||||O||OOO|||O|OO|OO||OO||OOOO|O|"

print "--FLAG LIST--"
for i0 in keylist[0] :
	for i1 in keylist[1] :
		for i2 in keylist[2] :
			for i3 in keylist[3] :
				for i4 in keylist[4] :
					for i5 in keylist[5] :
						for i6 in keylist[6] :
							for i7 in keylist[7] :
								flag = []
								for time in range(27) :
									for letter in maps :
										tmp_plain = bytearray( letter * 27 )
										key = bytearray(i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7)
										out = calculate_out(tmp_plain, key)
										if( out[time*8:time*8+8] == enc_flag[time*8:time*8+8]) :
											flag.append(letter)
									if len(flag) != (time + 1) :
										break
								if len(flag) == 27 :
									print ''.join(flag)



위 코드는 문제를 풀기위해 짠 decrypt코드이다. 좀 지저분한것같다;;


일단 복호화를 하기 위해 key를 알아야 하는데, key를 알기 위해서 

plain = bytearray("asdfghjk123456") 일 때 out이 문제에 제시된것과 같게 되는 key를 찾았다.


암호화 하는 과정을 보면 t1과 t2의 길이는 같고 out은 encode함수에 t2를 한글자씩 넣었을 때의 결과들의 합이다.

브루트포싱으로 key를 구할 수 있는데, 그 이유는 t2[i]연산에서 key와 1대 1로 xor연산을 하기 때문이다. (t2[0~7] ^= key[0~7]) 


key의 길이가 8이기 때문에 브루트포싱을 통해 key 8자리를 금방 알아낼수있다.


key를 알아낸 후 flag또한 브루트포싱을 통해 금방 알아낼수있다.


maps(82) * flag_len(27) * 모든 key배열의 경우의수(480) = 약 100만번인데, 

 

if len(flag) != (time + 1) :    

break


이 조건이 들어감으로써 대략 40000번 만에 flag목록을 뽑아낼 수 있다.




정확하게 SSCTF{} 포멧에 맞게 나온 것이 FLAG 이다.


























블로그의 정보

튜기's blogg(st1tch)

St1tch

활동하기