[MatesCTF:The Numbers] Using Z3 to solve math equations. 17 Jun 2018
Intro.
Hello! This is wire-up from Mates CTF for numbers
task.
We are given by numbers.exe binary, that wanted from us to give some numbers:
>numbers.exe
----- The Numbers -----
Designed by Quang Nguyen(quangnh89), a member of PiggyBird. My blog: https://develbranch.com
Enter the first part (8 numbers):
Welcome to floating point data.
If we open exe file in IDA, we will see a lot of SSE instructions:
.text:0000000000401517 lea rcx, [rbp-11h] ; Format
.text:000000000040151B call scanf
.text:0000000000401520 movd xmm0, cs:dword_403098
.text:0000000000401528 movq xmm1, qword ptr [rbp-64h]
.text:000000000040152D ucomiss xmm1, xmm0
.text:0000000000401530 jp loc_402163
.text:0000000000401536 jbe loc_402163
.text:000000000040153C movd xmm0, cs:dword_40309C
.text:0000000000401544 ucomiss xmm0, dword ptr [rbp-64h]
.text:0000000000401548 jp loc_402163
.text:000000000040154E jbe loc_402163
.text:0000000000401554 movd xmm0, cs:dword_4030A0
.text:000000000040155C movq xmm1, qword ptr [rbp-68h]
.text:0000000000401561 ucomiss xmm1, xmm0
.text:0000000000401564 jp loc_402163
.text:000000000040156A jbe loc_402163
.text:0000000000401570 movd xmm0, cs:dword_4030A4
.text:0000000000401578 ucomiss xmm0, dword ptr [rbp-68h]
To understand what is going on, you need to know how floating point values are saved in memory. It is very simple to view data in windbg:
0:000> dd 403098
00000000`00403098 00000000 437f0000 00000000 437f0000
00000000`004030a8 00000000 437f0000 00000000 437f0000
00000000`004030b8 00000000 437f0000 00000000 437f0000
00000000`004030c8 00000000 437f0000 00000000 437f0000
0:000> df 403098
00000000`00403098 0 255 0 255
00000000`004030a8 0 255 0 255
00000000`004030b8 0 255 0 255
00000000`004030c8 0 255 0 255
As you can see, DWORD data 0x437f0000
is corresponding to 255 integer. So, the next code basically checks that value on stack is < 255
:
.text:000000000040153C movd xmm0, cs:dword_40309C # 0x437f0000 = 255
.text:0000000000401544 ucomiss xmm0, dword ptr [rbp-64h]
.text:0000000000401548 jp loc_402163
.text:000000000040154E jbe loc_402163
Next, there is some mathematical equations is checked against the input data. For example the next code is checked (x5 + x6)*(x5 + x6) + x4*x4 == 153844
.text:00000000004016C0 movd xmm0, dword ptr [rbp-74h]
.text:00000000004016C5 addss xmm0, dword ptr [rbp-78h]
.text:00000000004016CA movd dword ptr [rbp-84h], xmm0
.text:00000000004016D2 movd xmm0, dword ptr [rbp-74h]
.text:00000000004016D7 addss xmm0, dword ptr [rbp-78h]
.text:00000000004016DC movq xmm1, xmm0
.text:00000000004016E0 movd xmm0, dword ptr [rbp-84h]
.text:00000000004016E8 mulss xmm0, xmm1
.text:00000000004016EC movd dword ptr [rbp-88h], xmm0
.text:00000000004016F4 movd xmm0, dword ptr [rbp-70h]
.text:00000000004016F9 mulss xmm0, dword ptr [rbp-70h]
.text:00000000004016FE movq xmm1, xmm0
.text:0000000000401702 movd xmm0, dword ptr [rbp-88h]
.text:000000000040170A addss xmm0, xmm1
.text:000000000040170E movd dword ptr [rbp-8Ch], xmm0
.text:0000000000401716 movd xmm0, cs:dword_4030D8
.text:000000000040171E ucomiss xmm0, dword ptr [rbp-8Ch]
.text:0000000000401725 jp loc_402163
.text:000000000040172B jnz loc_402163
0:000> df 4030D8
00000000`004030d8 153844
Using Z3 solver.
There is a lot of this kind of checks, you need to reverse all of it. Finally, you will get a list like this, for the first 8 input values:
x1 < 256, x2 < 256, x3 < 256, x4 < 256, x5 < 256, x6 < 256, x7 < 256, x8 < 256,
(x5 + x6)*(x5 + x6) + x4*x4 == 153844,
(x5 + x6)*(x5 + x6) + x3*x3 == 131400,
x5*x5 - x1*x1 == 181,
x6*x6 - x2*x2 == 46717,
x1 * x4 == 19080,
x2 * x3 == 15300,
x1 * x5 + x1 * x6 - 119 * x5 == 18871,
x2 * x6 + x2 * x5 - 70 * x6 == 16930,
x4 * x5 - x3 * x6 == -16558,
x1 * x2 - x7 == 9043,
x7 * x8 == 4247
The main question is how to solve this? The first my idea was to bruteforce all of data of 0..255 for each value. Luckily enough there is a much more intelligent way. The way is to use awesome Z3 solver.
Indeed, all you need is to run this script:
#!/usr/bin/env python
from z3 import *
x1 = Int('x1')
x2 = Int('x2')
x3 = Int('x3')
x4 = Int('x4')
x5 = Int('x5')
x6 = Int('x6')
x7 = Int('x7')
x8 = Int('x8')
solve(x1 < 256, x2 < 256, x3 < 256, x4 < 256, x5 < 256, x6 < 256, x7 < 256, x8 < 256,
(x5 + x6)*(x5 + x6) + x4*x4 == 153844,
(x5 + x6)*(x5 + x6) + x3*x3 == 131400,
x5*x5 - x1*x1 == 181,
x6*x6 - x2*x2 == 46717,
x1 * x4 == 19080,
x2 * x3 == 15300,
x1 * x5 + x1 * x6 - 119 * x5 == 18871,
x2 * x6 + x2 * x5 - 70 * x6 == 16930,
x4 * x5 - x3 * x6 == -16558,
x1 * x2 - x7 == 9043,
x7 * x8 == 4247)
# res : 90 102 150 212 91 239 137 31
solve(x1 < 256, x2 < 256, x3 < 256, x4 < 256, x5 < 256, x6 < 256, x7 < 256, x8 < 256,
(x4 + x6) * (x5 + x7) + (x5 * x4) == 43907,
x1 * x2 + x5 == 12563,
(x1 + x6) * (x2 + x6) + x3 * x3 == 130348,
x5 * x1 - x1 * x2 == -10682,
x4 * x6 - x2 * x3 == -9474,
x1 * x4 == 15484,
x2 * x3 == 32384,
x1 * x6 - 27 * x5 + x2 * x6 == 32257,
x2 * x7 - 74 * x3 + x1 * x2 == 8670,
x3 * x4 - x8 * x7 == 28838,
x1 * x3 + x7 == 24910,
x7 * x8 == 11136)
# res: 98 128 253 158 19 145 116 96
We can check this values, and get the flag!
Designed by Quang Nguyen(quangnh89), a member of PiggyBird. My blog: https://develbranch.com
Enter the first part (8 numbers):
90 102 150 212 91 239 137 31
Enter the second part (8 numbers):
98 128 253 158 19 145 116 96
The flag is matesctf{All you need is SMT!!}