【总结】逻辑运算在Z3中运用+CTF习题
2025-1-15 08:19:40 Author: www.secpulse.com(查看原文) 阅读量:1 收藏

国际赛IrisCTF在前几天举办,遇到了一道有意思的题目,特来总结。

题目

附件如下:babyrevjohnson.tar

解题过程

关键main函数分析如下:

int __fastcall main(int argc, const char **argv, const char
 **envp)
 {
 int v4;
 int v5;
 int v6;
 int v7;
 char input[104];
 unsigned __int64 v9;
 v9 = __readfsqword(0x28u);
 puts("Welcome to the Johnson's family!");
 puts("You have gotten to know each person decently well, so let's see
 if you remember all of the facts.");
 puts("(Remember that each of the members like different things from
 each other.)");
 v4 = 0;
 while ( v4 <= 3 )
 {
 printf("Please choose %s's favorite color: ", (&names)[v4]);
 4个人
 __isoc99_scanf("%99s", input);
 if ( !strcmp(input, colors) )
 {
 v6 = 1;
 goto LABEL_11;
 }
 if ( !strcmp(input, s2) )
 {
 v6 = 2;
 goto LABEL_11;
 }
 if ( !strcmp(input, off_4050) )
 {
 v6 = 3;
 goto LABEL_11;
 }
 if ( !strcmp(input, off_4058) )
 {
 v6 = 4;
 LABEL_11:
 if ( v6 == chosenColors[0] || v6 == dword_4094 || v6 ==
 dword_4098 || v6 == dword_409C )
 puts("That option was already chosen!");
 else
 chosenColors[v4++] = v6;
 }
 else
 {
 puts("Invalid color!");
 }
 }
 v5 = 0;
 while ( v5 <= 3 )
 {
 printf("Please choose %s's favorite food: ", (&names)[v5]);
 4个人最喜欢的食物
 __isoc99_scanf("%99s", input);
 if ( !strcmp(input, foods) )
 {
 v7 = 1;
 goto LABEL_28;
 }
 if ( !strcmp(input, off_4068) )
 {
 v7 = 2;
 goto LABEL_28;
 }
 if ( !strcmp(input, off_4070) )
 {
 v7 = 3;
 goto LABEL_28;
 }
 if ( !strcmp(input, off_4078) )
 {
 v7 = 4;
 LABEL_28:
 if ( v7 == chosenFoods[0] || v7 == dword_40A4 || v7 == dword_40A8
 || v7 == dword_40AC )
 puts("That option was already chosen!");
 else
 chosenFoods[v5++] = v7;
 }
 else
 {
 puts("Invalid food!");
 }
 }
 check();
 return 0;

 }
 -----------------------------------------------------------------------

将check提取出来,我们方便分析

其实到这里已经可以得到结果了,国外的题目确实很讲究趣味性,用颜色和食物作为导向,引导一步一步分析

笔者使用静态分析的方法,一步一步跟踪

C++

 int check()
 {
 bool v0;
 _BOOL4 v1;
 _BOOL4 v2;
 v0 = dword_40A8 != 2 && dword_40AC != 2;
 
 v1 = v0 && dword_4094 != 1;
 v2 = chosenColors[0] != 3 && dword_4094 != 3;
 if ( !v2 || !v1 || chosenFoods[0] != 4 || dword_40AC == 3 ||
 dword_4098 == 4 || dword_409C != 2 )
 return puts("Incorrect.");
 puts("Correct!");
 return system("cat flag.txt");

 }
 -----------------------------------------------------------------------

对应的输入值地址如下:

我们将颜色color数组用x系列表示,将食物用food数组y系列表示

化简如下:

 C++
 v0 = y3 != 2 && y4 != 2;
 
 v1 = v0 && x2 != 1;
 v2 = x1 != 3 && x2 != 3;
 if ( !v2 || !v1 || y1 != 4 || y4 == 3 || x3 == 4 || x4 != 2
 )
 {
 
 }
 else
 {
 

 }
 -----------------------------------------------------------------------

思路1:简单粗暴的爆破,但不是学习的目的,因此并不采用

思路2:锻炼写脚本能力,使用z3解题可以锻炼写脚本的能力,因此采用

Python

  from z3 import *
 
 
 x1, x2, x3, x4 = Ints('x1 x2 x3 x4')
 y1, y2, y3, y4 = Ints('y1 y2 y3 y4')
 
 
 v0 = And(y3 != 2, y4 != 2)
 v1 = And(v0, x2 != 1)
 v2 = And(x1 != 3, x2 != 3)
 
 
 cond = Or(Not(v2), Not(v1), y1 != 4, y4 == 3, x3 == 4, x4 != 2)
 cond1 = Not(cond)
 
 
 solver = Solver()
 
 
 solver.add(cond1)
 
 
 if solver.check() == sat:
 
 model = solver.model()
 
 
 print("成功:")
 print("x1 =", model[x1])
 print("x2 =", model[x2])
 print("x3 =", model[x3])
 print("x4 =", model[x4])
 print("y1 =", model[y1])
 print("y2 =", model[y2])
 print("y3 =", model[y3])
 print("y4 =", model[y4])
 else:

 print("无解")
 ---------------------------------------------------------------------------------------

得到结果

Python

  成功:
 x1 = 4
 x2 = 0
 x3 = 5
 x4 = 2
 y1 = 4
 y2 = None
 y3 = 3

 y4 = 0
 -----------------------------------------------------------------------

其实有经验的师傅发现了,这是有多解的,因为没有为约束变量添加范围约束

改进之后的代码如下:

Python

  from z3 import *
 
 
 x1, x2, x3, x4 = Ints('x1 x2 x3 x4')
 y1, y2, y3, y4 = Ints('y1 y2 y3 y4')
 
 
 v0 = And(y3 != 2, y4 != 2)
 v1 = And(v0, x2 != 1)
 v2 = And(x1 != 3, x2 != 3)
 range_constraint = And(x1 >= 1, x1 <= 4, x2 >= 1, x2 <= 4, x3 >= 1, x3 <= 4, x4
 >= 1, x4 <= 4,
 y1 >= 1, y1 <= 4, y2 >= 1, y2 <= 4, y3 >= 1, y3 <= 4, y4 >= 1, y4 <= 4)
 
 cond = Or(Not(v2), Not(v1), y1 != 4, y4 == 3, x3 == 4, x4 != 2)
 cond1 = Not(cond)
 
 
 solver = Solver()
 
 
 solver.add(cond1)
 solver.add(range_constraint)
 
 if solver.check() == sat:
 
 model = solver.model()
 
 
 print("成功:")
 print("x1 =", model[x1])
 print("x2 =", model[x2])
 print("x3 =", model[x3])
 print("x4 =", model[x4])
 print("y1 =", model[y1])
 print("y2 =", model[y2])
 print("y3 =", model[y3])
 print("y4 =", model[y4])
 else:

 print("无解")
 ---------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------

得到结果:

-----------------------------------------------------------------------

 Python
 成功:
 x1 = 1
 x2 = 4
 x3 = 1
 x4 = 2
 y1 = 4
 y2 = 1
 y3 = 3

 y4 = 4
 -----------------------------------------------------------------------

发现x1和x3重复了,因此还要添加值不重复约束

 Python
 from z3 import *
 
 
 x1, x2, x3, x4 = Ints('x1 x2 x3 x4')
 y1, y2, y3, y4 = Ints('y1 y2 y3 y4')
 
 
 v0 = And(y3 != 2, y4 != 2)
 v1 = And(v0, x2 != 1)
 v2 = And(x1 != 3, x2 != 3)
 
 range_constraint = And(x1 >= 1, x1 <= 4, x2 >= 1, x2 <= 4, x3 >= 1, x3 <= 4, x4
 >= 1, x4 <= 4,
 y1 >= 1, y1 <= 4, y2 >= 1, y2 <= 4, y3 >= 1, y3 <= 4, y4 >= 1, y4 <= 4)
 
 distinct_x=Distinct(x1,x2,x3,x4)
 distinct_y=Distinct(y1,y2,y3,y4)
 
 
 cond = Or(Not(v2), Not(v1), y1 != 4, y4 == 3, x3 == 4, x4 != 2)
 cond1 = Not(cond)
 
 
 solver = Solver()
 
 
 solver.add(cond1)
 solver.add(range_constraint)
 solver.add(distinct_y)
 solver.add(distinct_x)
 
 if solver.check() == sat:
 
 model = solver.model()
 
 
 print("成功:")
 print("x1 =", model[x1])
 print("x2 =", model[x2])
 print("x3 =", model[x3])
 print("x4 =", model[x4])
 print("y1 =", model[y1])
 print("y2 =", model[y2])
 print("y3 =", model[y3])
 print("y4 =", model[y4])
 else:

 print("无解")
 ---------------------------------------------------------------------------------------

最终得到正确的结果

Python 成功: x1 = 1 x2 = 4 x3 = 3 x4 = 2 y1 = 4 y2 = 2 y3 = 3

y4 = 1

x1-x4= 1 4 3 2

y1-y4= 4 2 3 1

按照这样的顺序输入即可:

得到了flag

irisctf{m0r3_th4n_0n3_l0g1c_puzzl3_h3r3}

总结

题目并不是很难,没有复杂的ollvm混淆也没有复杂的加密。但是却一步一步引导我们去学习和总结。z3解题的过程中,会有很多误解,然后经过自己的思考总结,发现了漏掉的东西,再进行补充,最终写出正确的脚本。

国外的题还是很值得学习的,不单单为了出题而出题。这就是逻辑运算在z3的运用以及如何增加约束,让z3求解出我们需要的key。

本文作者:[email protected]

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/205237.html


文章来源: https://www.secpulse.com/archives/205237.html
如有侵权请联系:admin#unsafe.sh