menu 牢记自己是菜
GACTF2020赛后总结与部分题解
3334 浏览 | 2020-08-31 | 阅读时间: 约 4 分钟 | 分类: | 标签:
请注意,本文编写于 1306 天前,最后修改于 1306 天前,其中某些信息可能已经过时。

0x1 前言

哇,48小时的超长比赛,48小时的超长马拉松,感谢师傅们的用心努力,居然带着我们冲进了前20。可喜可贺,可喜可贺,这次比赛好像没学长,只有我们自己,看来BXS有救了,当然救星指的是师傅们。这次比赛我主要肝了两道逆向题目,和一道密码学。对了这里就要说一下我们队伍里的密码学奶奶了,emmmmm完全没有。之前分方向的时候说每个人都要会密码学,现在好了,所有人都不大会。这里主要是做一下这几道题目的记录,总体的来说我觉得这次的逆向难度并没有之前的题目难度大,但是脑洞倒是挺大的,毕竟第二道题目把LC-3汇编都给安排来了,查到的资料都是2014年左右的东西。话不多说,我们开始吧。


0x2 Checkin

这是本次比赛的第一道re题目,不算是很常规的加密题目,首先我们运行一下程序,出现了一个Input:然后输入,输入错误程序退出。一开始我以为是一道很常规的题目,首先打开IDA进行分析,但是我们没有找到任何有用的信息。但是我们也没有发现很明显的反调试手段,所以我们使用OD进行动态的分析。跟了很多个来回,一无所获,既没有发现关键信息,也没有搞清楚这玩意是怎么输入的。但是发现了一个很好玩的地方,就是他一直在获取我的文件路径,PATH等信息,结合题目所给提示“建议虚拟机运行”,我才反应过来这应该是一个被阉割的病毒。
对于病毒的分析,我只看过一个案例,就是在《有趣的二进制》这本书中的第一个例子。当时的病毒是一个注册表病毒,分析手段是监视该进程的工作流程。这里就要介绍到一个工具了--processmonitor。这是一个动态监控电脑中的所有文件资源的软件,它可以动态的反应每一个软件的宏观过程,包括对内存的读写,文件的创建于删除等。
细心的同学可能会发现这玩意是没有头的,因为他是动态记录各种程序的流程的,相当与电脑的一个实时日志。首先我们来介绍一下这个玩意。

是不是简单易懂,其实在我们运行程序的时候,其实很容易可以发现,在它弹出enter flag之前,会出现一个小卡顿。我们使用进程管理器追踪一下,我们的程序,果不其然我们发现了一个创建文件的操作,记住路径地址,我们去看看他到底创建了一个啥玩意。

我们跟踪到对应的路径,我们找到了那个文件夹,从中找到了一个python的脚本文件,是一个AES算法。

require 'openssl'  
require 'base64'  

 
def aes_encrypt(key,encrypted_string)
    aes = OpenSSL::Cipher.new("AES-128-ECB")
    aes.encrypt
    aes.key = key
    cipher = aes.update(encrypted_string) << aes.final
    return Base64.encode64(cipher) 
end

print "Enter flag: "
flag = gets.chomp

key = "Welcome_To_GACTF"
cipher = "4KeC/Oj1McI4TDIM2c9Y6ahahc6uhpPbpSgPWktXFLM=\n"

text = aes_encrypt(key,flag)
if cipher == text
    puts "good!"
else
    puts "no!"
end

完成解密即可得到flag。

0x3 Simulator

这道题目感觉就是上一次XCTF的那道题目的简化版,题目最难的地方就是猜他到底给了个啥玩意。上一次毛提示都没有,给了一个老掉牙虚拟机的配置文件,这次给了一个LC3的汇编程序(同样是个老古董)。但是这次起码给了点提示,两种方法可以知道这道题目是LC3的汇编。

  • 使用TXT文件或者winhex打开文件,可以看见它所提供的字符串,其中有LC3的提示。
  • 根据提示:“Study 《Introduction to Computer System》 carefully”,去找这本书(图书馆里面有电子版的哦)。这本书是从底层向上介绍计算机的,仔细看所有的章节目录,只有LC3这一个介绍章节很陌生。其余的不是操作系统学过就是微机原理接口学过,所以很好排除。

这里我们要用到LC3Edit,这个是专门来编写LC3的软件。稍微介绍一下:


其余的部分与上次的题目很像,动手调一调就行了。这种语言我认为最难理解的就是条件跳转语句,只要掌握了这个语句,整个程序都会变得十分简单。这个指令在书中有提到,细节看书查查就行了。这里只简单的说几句:

  • BR(Z)开头的就是跳转,类似汇编中的j(Z)
  • BR也会判断几个标志位分别是Z,N,P。
  • 条件变化方式与汇编基本相同
  • BRNZP是无条件跳转类似jmp,因为他同时检测三个标志位。

这里是关键的跳转,盯着这个看就行了:

加密算法和上次异曲同工,通过and,not等指令实现XOR。实际上就是每次取两位输入(从最后一个开始向前),进行抑或,在与内存中的数据比较,正确进入下一轮,错误跳出。
python生成所有的可能,从中选一下像样的。(其实不用因为他最后一位规定必须是l(108),这是我之后才发现的):

A=[17,17,9,28,29,2,12,60,43,1,23,61,51,0,13,12,30,44,44,66,110,108,80,15,108]
for i in range(0x21,0xe7):
    H=i
    print()
    for j in range(0,25):
        print(chr(H),end="")
        H=H^A[j]
        
    #if (H^i)==108:
     #   print(chr(i))
      #  break
B="etelpmoc_tub_llams_s1_3cl"#这里只是我想区分一下L和1
for i in B:
    print(ord(i))

由于是反着求出来的,最后将他正过来就好了。


0x4 what_r_the_noise

差分隐私,一种保护用户数据的方法。差异隐私用于以某种方式对数据进行编码,以至于您无法辨认任何单人输入,同时保持数据的实际统计结果不变。我们还原flag的方法也很简单,就是获得很多组数据,然后平均他。就好像我们扔硬币,只要次数够多,正反面的概率接无限接近0.5一样。脚本

from pwn import *
import ast

host = "124.71.145.165"
port = 9999
#context.log_level='debug'
sample = 7   #这里将次数设置为8


def get_list():#获取数据
  conn.recvuntil(":")
  conn.sendline("2")
  h=conn.recvline()
  #print h
  data = ast.literal_eval(h.decode().strip())
  print data
  return data

gotten = None
conn = remote(host,port)
while True: 
  d = get_list()
  #get_list()
  if not gotten:#创建很多个元组来保存数据
    gotten = [[] for j in d]

  for j,v in enumerate(d):#将对应位数上的
    gotten[j].append(v)

  if len(gotten[0]) > sample + 1:#当超过8组的时候
    r = max([1,int((len(gotten[0])-sample)/2)])
    for vals in gotten:
      work = vals[:]
      work = sorted(work)[r:-r]
      print chr(int(round(sum(work)/len(work)))),#处理数据并打印,不停的处理直至稳定在一组值上,这组值就是原先的flag
    print(" ", len(gotten[0]))#显示已经处理了多少组数据


大概处理了200次左右,数据就稳定在了flag处。

发表评论

email
web

全部评论 (共 5 条评论)

    T4
    2021-08-04 10:36
    从吾爱过来膜拜大佬
    2020-09-05 11:14
    lsp的博客我只看封面
      2020-09-07 16:27
      @qingtian这就是物以类聚嘛^_^
    2020-08-31 20:23
    我就说嘛,你群里说的看log是什么意思,原来说的是processmonitor啊。学到了学到了~
      2020-09-01 10:54
      @iyzyiヾ(≧∇≦*)ゝ