주어진 zip 파일을 받아서 압축을 해제하면 문제 바이너리와 tar 파일이 나온다.
tar를 또 해제하면 아래와 같이 엄청나게 많은 라이브러리 파일이 들어있다.
정말 많은 양의 라이브러리가 있다.
우선 바이너리를 실행했다.
실행을 하면 처음에 입력을 한번 받고 두번 째 입력을 받는다.
얼추 유추해보면 처음 받는 수는 라이브러리이름 넘버를 받는 것으로 보인다.
두 번째 입력은 뭔가를 받는데 반응이 없다.
이번엔 다른 값을 넣어보았다.
이번엔 라이브러리를 임의로 55를 넣었다.
그리고 이번엔 '\\\\\\'를 넣었더니 명령어 에러 메시지가 나왔다.
두 번째 입력은 ls 뒤에 들어가도록 된 듯 하다.
이제 ida로 확인해보자.
2000의 main함수다.
input으로 라이브러리의 숫자를 받는것을 볼 수 있고,
해당 라이브러리의 test라는 함수의 주소를 가져와 실행시킨다.
나는 55를 넣었으니 lib_55.so 파일도 ida로 열었다.
lib_55.so에서는 lib_9068.so의 filter1 함수를 가져오고, lib_8658.so의 filter2함수를 가져온다.
그리고 system함수의 인자로 ls ""를 준다. 내가 입력한 값은 저 ls의 옵션 부분으로 들어간다.
lib_9068.so의 filter1함수다. 입력값의 필터로 보인다.
libc_8658.so의 filter2다.
f,l,g, bin, sh, bash가 필터로 막혀있다.
두 함수 다 두 번째 입력에 대한 필터 들이다.
이 부분을 우회 해야하는데 우회법을 찾지 못했고, 다른분의 라업을 보면 r2pipe라는 파이썬 라이브러리를 이용했다.
r2pipe는 radare2라는 리버싱 툴을 파이썬으로 사용 가능 하도록 한 API이다.
이 많은 라이브러리들 중 필터가 다른 라이브러리를 찾아서 문제를 풀었다.
import r2pipe
import ast
test = []
filter1 = []
filter2 = []
for i in range(1, 20000+1):
r2 = r2pipe.open('./lib_{}.so'.format(i))
if 'dlopen' in r2.cmd('ii'):
test.append(i)
if 'filter1' in r2.cmd('is'):
filter1.append(i)
if 'filter2' in r2.cmd('is'):
filter2.append(i)
if i % 12 == 0:
print i
r2.quit()
r = open('test', 'w')
r.write(str(test))
r.close()
r = open('filter1', 'w')
r.write(str(filter1))
r.close()
r = open('filter2', 'w')
r.write(str(filter2))
r2.cmd('ii')는 import 리스트를 출력하는 명령이고,
r2.cmd('is')는 symbol 리스트를 출력하는 명령이다.
해당 명령 설명은 radare2를 실행시켜 명령을 보면 나온다.
위 코드로 filter1, filter2를 가지고 있는 라이브러리 파일들의 리스트를 각각 추출해서 파일로 저장했다.(너무 오래 걸리기 때문에..)
이제 그 리스트로 lib_55.so의 filter1과 다른 필터를 가진 라이브러리를 찾을 것이다.
test = open('./test', 'r').read()
filter1 = open('./filter1', 'r').read()
filter2 = open('./filter2', 'r').read()
test = ast.literal_eval(test)
filter1 = ast.literal_eval(filter1)
filter2 = ast.literal_eval(filter2)
diff = []
for i in filter1:
r2 = r2pipe.open('./lib_{}.so'.format(i))
r2.cmd('aaa')
filters = ['0x3b', '0x2a', '0x7c', '0x26', '0x24', '0x60', '0x3e', '0x3c', '0x72']
out = r2.cmd('pdf @ sym.filter1')
for filter in filters:
if filter not in out:
ff.append(i)
break
r2.quit()
print diff
각 라이브러리 filter1의 어셈블 코드를 받아 55의 필터와 비교하고 없다면 찾아내는 코드다.
lib_4323.so만 다르다고 나왔다.
filter1을 보니 | 문자가 필터에 없었다.
이번엔 이 lib_4323을 불러오는 test()함수를 가진 라이브러리를 찾아보자.
file = []
for i in test:
r2 = r2pipe.open('./lib_{}.so'.format(i))
if "./20000_so/lib_4323.so" in r2.cmd('iz'):
print "find"
file.append(i)
if (i % 12 == 0):
print i
r2.quit()
print file
r2.cmd('iz')는 해당 바이너리의 string을 찾아내는 명령이다.
lib_17394.so가 나왔다.
lib_17394.so를 ida로 보면 lib_4323.so를 dlopen하고 system("입력값" 2 > /dev/null)을 실행한다. 아까 lib_55.so와는 많이 다른 코드를 가지고 있다.
여기서는 또 lib_11804.so를 dlopen한다. 다시 ida로 lib_11804.so을 열어보자.
아까 본 filter2와는 다른 필터를 가지고 있다. 여기는 bin, sh 다 필터에 없다.
따라서 lib_17394.so를 실행시키면 system("sh")을 실행시킬 수 있다!