LakeCTF 2022 - baby rev (rev, z3)
분석 환경: Windows 11, Ubuntu 20.04
사용자 제공 파일: 바이너리, nc 접속 주소
평소에 리버싱 분야를 잘 풀지 않는데, 뭔가 오늘따라 풀어보고 싶어서 솔브가 많은 리버싱 하나 열어봤다.
Analysis
IDA로 열면 main함수가 있는데 대충 핵심적인 부분(밖에 없지만)을 가져오면 아래와 같다.
1 | __printf_chk(1LL, "What's your your number:", envp); |
사용자 입력을 받고 그 입력에 대해 ‘어떠한 연산’을 진행한 결과(key)와 비교해서 맞으면 flag를 출력해준다.
브루트 포싱을 하면 되지 않을까? 이 코드를 c로 옮겨서 브포해봐야겠다.
Solve
1 | // gcc -o solve solve.c |
이런식으로 c로 코드를 옮기고 루프를 돌려주면 아래와 같이 98304
라는 값이 나온다. 사실 이 문제 정답을 여러개인데 하나만 입력하면 flag를 던져주기 때문에 하나만 출력하고 바로 break를 해주었다.
1 | jir4vvit@ubuntu:~/ctf/lake/rev-baby$ ./solve |
More
이 문제를 파이썬 모듈인 Z3 solver
를 이용하여 풀 수도 있다.
Z3 solver
Z3 solver
는 특정 값, 즉 정답을 찾아주는 SMT solver 모듈이라고 한다. (SMT: Satisfiability Modulo Theories)
어떠한 수식을 만족하는 값이 존재하는 지 찾아주는 건데, 존재한다면 그 값을 구해준다.
1 | x + y = 12 |
예를 들어, 이런 연립방정식이 존재할 때, x와 y의 값을 Z3 solver
가 구해줄 수 있다.
바로 아래와 같이..
1 | >>> from z3 import * |
아래에 다운로드 방법과 여러 링크들을 남기고 Z3를 이용하여 이 문제를 어떻게 풀 수 있는지 알아보자.
다운로드 방법
1 | pip install z3-solver |
solve with Z3
일단. 모듈을 가져오고 변수 두 개를 선언해야 한다.
1 | from z3 import * |
실수들이.. 주석처리되어 있는데, 가장 처음 했던 실수는 사용자 인풋을 그냥 Int()
로 정의한 점이다.BitVec("", 비트수)
로 정의해야 한다. 왜냐하면 이 문제에서 사용자 인풋을 가지고 비트연산을 진행하는데 Int()
로 선언하면 TypeError가 발생하기 때문이다.
1 | TypeError: unsupported operand type(s) for >>: 'instance' and 'int' |
그래서 비트연산을 할 때는 비트연산을 위한 BitVec()
으로 선언을 해줘야 한다.
두 번째 했던 실수는 단순하다. 그냥 미지수 아닌데 미지수로 선언해줬다. 그러면 안된다.
1 | for v3 in range(0, 32): |
파이썬으로 연산 코드를 작성해준다.
1 | s = Solver() |
Z3 solver
는 Solver
객체를 지원해준다. 이 객체를 사용해보자. s.add(수식)
로 ipt에 대한 제약 조건 및 값을 구하는 조건도 추가해줬다.
1 | while s.check() == sat: |
s.check()
는 해당 방적식을 만족하는 값(정답, 해)이 있는지 체크해준다. 값이 있으면 sat
, 없으면 unsat
, 해결할 수 없으면 unknown
을 반환한다고 한다.
이 문제는 값이 여러 개가 나오므로 중복 체크도 해주었다. 방금 구한 ipt의 값을 제외한 다른 값을 구할 수 있다.
s.model()
은 s.check()
가 sat
일 때 만족하는 해 중 하나를 반환한다.
최종 코드
1 | from z3 import * |