【總結】邏輯運算在Z3中運用+CTF習題

蚁景网安实验室發表於2024-07-17

國際賽IrisCTF在前幾天舉辦,遇到了一道有意思的題目,特來總結。

【總結】邏輯運算在Z3中運用+CTF習題

題目

【總結】邏輯運算在Z3中運用+CTF習題

附件如下:📎babyrevjohnson.tar

解題過程

關鍵main函式分析如下:


int __fastcall main(int argc, const char **argv, const char
  **envp)
  {
  int v4; // [rsp+4h] [rbp-7Ch]
  int v5; // [rsp+4h] [rbp-7Ch]
  int v6; // [rsp+8h] [rbp-78h]
  int v7; // [rsp+Ch] [rbp-74h]
  char input[104]; // [rsp+10h] [rbp-70h] BYREF
  unsigned __int64 v9; // [rsp+78h] [rbp-8h]
  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 ) // 在提供的顏色中,選擇4種
  {
  printf("Please choose %s's favorite color: ", (&names)[v4]);//
  4個人
  __isoc99_scanf("%99s", input);
  if ( !strcmp(input, colors) )
  {
  v6 = 1; // red
  goto LABEL_11;
  }
  if ( !strcmp(input, s2) )
  {
  v6 = 2; // blue
  goto LABEL_11;
  }
  if ( !strcmp(input, off_4050) )
  {
  v6 = 3; // green
  goto LABEL_11;
  }
  if ( !strcmp(input, off_4058) )
  {
  v6 = 4; // yellow
  LABEL_11:
  if ( v6 == chosenColors[0] || v6 == dword_4094 || v6 ==
  dword_4098 || v6 == dword_409C )// 選擇4個顏色,然後順序不能一樣
  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; // pizza
  goto LABEL_28;
  }
  if ( !strcmp(input, off_4068) )
  {
  v7 = 2; // pasta
  goto LABEL_28;
  }
  if ( !strcmp(input, off_4070) )
  {
  v7 = 3; // steak
  goto LABEL_28;
  }
  if ( !strcmp(input, off_4078) )
  {
  v7 = 4; // chicken
  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(); // 開始check,檢測我們輸入的顏色和食物是否正確
  return 0;
​
  }
  -----------------------------------------------------------------------

將check提取出來,我們方便分析

其實到這裡已經可以得到結果了,國外的題目確實很講究趣味性,用顏色和食物作為導向,引導一步一步分析

筆者使用靜態分析的方法,一步一步跟蹤


C++

 int check()
  {
  bool v0; // dl
  _BOOL4 v1; // eax
  _BOOL4 v2; // edx
  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"); // 執行cat flag的命令
​
  }
  -----------------------------------------------------------------------

對應的輸入值地址如下:

【總結】邏輯運算在Z3中運用+CTF習題

我們將顏色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)
  #正常來說,cond的值要為false的,但是z3的add新增的條件必須為1才行,因此要進行取反操作
  # 建立求解器
  solver = Solver()
  
  # 新增約束條件和條件語句到求解器
  solver.add(cond1)#這裡新增的條件必須為true,所以最後使用了 not 進行取反操作
  
  # 求解
  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
  -----------------------------------------------------------------------

其實有經驗的師傅發現了,這是有多解的,因為沒有為約束變數新增範圍約束

【----幫助網安學習,以下所有學習資料免費領!加vx:dctintin,備註 “部落格園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客戶端安全檢測指南(安卓+IOS)

改進之後的程式碼如下:


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)
  #正常來說,cond的值要為false的,但是z3的add新增的條件必須為1才行,因此要進行取反操作
  # 建立求解器
  solver = Solver()
  
  # 新增約束條件和條件語句到求解器
  solver.add(cond1)#這裡新增的條件必須為true,所以最後使用了 not 進行取反操作
  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)
  #正常來說,cond的值要為false的,但是z3的add新增的條件必須為1才行,因此要進行取反操作
  # 建立求解器
  solver = Solver()
  
  # 新增約束條件和條件語句到求解器
  solver.add(cond1)#這裡新增的條件必須為true,所以最後使用了 not 進行取反操作
  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

【總結】邏輯運算在Z3中運用+CTF習題

y1-y4= 4 2 3 1

【總結】邏輯運算在Z3中運用+CTF習題

按照這樣的順序輸入即可:

【總結】邏輯運算在Z3中運用+CTF習題

得到了flag

irisctf{m0r3_th4n_0n3_l0g1c_puzzl3_h3r3}

【總結】邏輯運算在Z3中運用+CTF習題

總結

題目並不是很難,沒有複雜的ollvm混淆也沒有複雜的加密。但是卻一步一步引導我們去學習和總結。z3解題的過程中,會有很多誤解,然後經過自己的思考總結,發現了漏掉的東西,再進行補充,最終寫出正確的指令碼。

國外的題還是很值得學習的,不單單為了出題而出題。這就是邏輯運算在z3的運用以及如何增加約束,讓z3求解出我們需要的key。

更多網安技能的線上實操練習,請點選這裡>>

相關文章