2019UNCTF竞技赛部分writeup
阅读原文时间:2023年07月08日阅读:2

Reverse

unctf_babyre2

这题flag分为两部分,第一部分每四个字符一组打包成int后,每次把四个int传入函数,后三个参数异或后先以每位为索引查表,将新数据进行循环移位、异或,将结果与第一个参数异或。此操作进行了26次,通过最后的一组结果可以慢慢倒推回去。第二部分是对字符串进行常规的异或操作与目标串比较。

脚本:

#include

#include

int main(){

int i,j;

unsigned int v40,v41,v42,v43,v44,v39,v1,v1a,v1b,v1c,v1d,part1d,index,v1rop;

int v5[]={,,,,,};

char part2[]={};

char part1[]={};

char c[]={0x2C,0x21,0x1e,0x73,0x32,0x12,0x72,0x37,0x10,0x38,0x38,0x1,0x1D,0x6B,0x66,0x79,0x79,0x26};

unsigned int road[]={};

unsigned int map[]={0X1B,0X5D,0X42,0X2B,0X0D,0X05,0X48,0XE6,0X35,0X16,0X9E,0XB5,0XBB,0XE3,0X24,0X0F,0X13,0XC0,0X59,0X96,0X5A,0X12,0X2B,0XE0,0X8F,0X21,0X8C,0X52,0XDE,0X92,0X12,0X84,0XA3,0XE2,0X6E,0X7B,0X76,0XA2,0X0F,0X51,0X93,0XA9,0X78,0XAB,0X5F,0X5E,0X16,0X82,0X72,0X82,0X26,0XD1,0X26,0XD4,0X09,0XBF,0X74,0XDA,0XA7,0X3E,0X99,0X02,0X65,0XC3,0XB3,0XAD,0XE0,0X5A,0XAB,0X7A,0X83,0X93,0X3F,0XA4,0X11,0X3D,0X8E,0X0D,0XDF,0X5A,0X71,0X08,0X3A,0XC8,0XF4,0X90,0X16,0X1B,0X88,0XC6,0X50,0X6F,0XD1,0XA4,0XB3,0X73,0X7B,0X82,0XBF,0XB2,0X5F,0X94,0XDE,0XCA,0X5A,0X5E,0XAB,0X25,0XBE,0X8C,0X1B,0X80,0X65,0X9E,0XEC,0X5A,0X37,0X2A,0X75,0X2C,0X2D,0XBA,0X56,0XD0,0XBA,0X3A,0XB6,0X94,0X81,0X70,0X87,0X75,0X3D,0X48,0X63,0X7D,0X52,0X81,0X39,0XB5,0X23,0XD4,0XD3,0XDD,0X4B,0XD9,0XB8,0X35,0XA3,0XCA,0X40,0X77,0X52,0X7C,0X9E,0X6C,0X42,0XD8,0X53,0X6F,0XEA,0X2E,0X0C,0X9A,0XF3,0X2A,0X6A,0XD5,0XEA,0X6B,0X93,0X2F,0X18,0X5C,0XBE,0X96,0XB4,0X26,0X0F,0XDB,0X9F,0X07,0X30,0XAF,0X93,0X34,0X27,0X8E,0X0A,0XCA,0X53,0XB7,0XC9,0X8F,0X9B,0X40,0X87,0X54,0X50,0X53,0X1E,0X55,0X06,0X04,0X87,0XC9,0X5E,0X78,0XA0,0X3F,0X66,0X08,0XB0,0X09,0X6E,0X83,0XE5,0X6C,0X23,0XE6,0X74,0X83,0X01,0XA4,0X7F,0X62,0X39,0X09,0X94,0X32,0XD3,0X88,0X93,0X61,0XC2,0XC6,0X61,0X6B,0X28,0XC7,0X61,0XDD,0XDB,0X90,0XA9,0XD5,0XD8,0X8A,0XA4,0XA0,0X65,0XC1,0X35,0X41,0XBA,0XCF,0X4A,0X47,0XCA,0XAF,0X51,0XE1,0X72,0X5A,0XBF,0X1E,0XB3,0X7A,0X80,0XF2,0X7A,0XCB,0X25,0XE6,0X98,0X96,0X1B,0X53,0X44,0XD8,0X3C,0XAC,0X12,0XB1,0X64,0X47,0X35,0X00,0XFF,0XFF,0XFF,0XFF};

for(i=;i<;i++){

c[i]^=0x45;

}

for (j=;j>=;j--){

part2[*(j/)+v5[j%]]=c[j];

}

for (j=;j<;j++){

part2[j]^=j;

}

//part1:

road[]=0XCC227F52;

road[]=0X5227AA48;

road[]=0X34725FD0;

road[]=0X0F276B39;

for(i=;i>=;i--){

index=road[i+]^road[i+]^road[i+];

v1=(map[index>>]<<)|(map[(index>>)&0xFF]<<)|(map[(index>>)&0xFF]<<)|map[index&0xFF];

v1rop=((v1>>)|(v1<<))^((v1>>)|(v1<<))^((v1<<)|(v1>>))^((v1<<)|(v1>>));

road[i]=road[i+]^v1rop;

}

strncpy(part1,(char *)road,);

printf("UNCTF{%s-%s}",part1,part2);

return ;

}

UNCTF{1_th1nk_re_e4sy!-Wh4t_aB0ut_yoU233?}

666

输入18位串,经过变换后和目标串izwhroz""w"v.K".Ni对比,这个可以逆推回去。

#include

int main(){

char i;

char target[]="izwhroz\"\"w\"v.K\".Ni";

for(i=;i<;i+=){

target\[i\]=(target\[i\]^)-;

target\[i+\]=(target\[i+\]^)+;

target\[i+\]=(target\[i+\]^)^;

}

puts(target);

return ;

}

输出:unctf{b66_6b6_66b}

BabyXor

这个需要用到一些动态调试技巧。

用OD载入,先运行起来,在待输入时按暂停。点击K查看堆栈调用,发现了ReadConsoleA这个函数,但这是系统api,没有价值,所以右击,显示调用,再右击选择执行到返回,再稍微往下走出retn,就能见到程序的调用代码。选中的call就是刚才的api。下面就是对输入的处理了。

紧接着有段代码读取输入:

再往后rern出去,有三个函数生成了flag:

flag{2378b077-7d6e-4564-bdca-7eec8eede9a2}

unctf_easy_Maze

IDA分析得知main里有三个主要函数,step0和step1用来生成一张7X7的地图,step2读取键位,并且判断当前地图是否为1.

生成地图的函数算法过于复杂,但可以通过动态调试看数据来直接看到结果。

在step2下断,程序运行到这里后进call,找到a1的地址,在数据窗口找到,把49个字节复制出来,转换成地图即可。

UNCTF{ssddwdwdddssaasasaaassddddwdds}

奇怪的数组

IDA查看后得知要求输入格式为flag{32位数据},其中32位数据为0-9和a-f。每次两个数字转成16进制数后和checkbox里的数比较,循环15次。所以把checkbox的数翻译成小写即可。

flag{ad461e203c7975b35e527960cbfeb06c}

easyvm

vm把函数入口和调用过程做的很复杂,经过判断后下图黄色代码原本是一个函数,功能是提供其他函数的入口。

把里面全部函数下断,看得出程序源码大概是这样的:

#include

int main(){

int i;

unsigned char a16=,a17=;

char input[]="";

unsigned char c[]={0xF4,0x0A,0xF7,0x64,0x99,0x78,0x9E,0x7D,

        0xEA,0X7B,0X9E,0X7B,0X9F,0X7E,0XEB,0X71,

        0XE8,0X00,0XE8,0X07,0X98,0X19,0XF4,0X25,

        0XF3,0X21,0XA4,0X2F,0XF4,0X2F,0XA6,0X7C};

for(i=;i<;i++){

a16=input[i];

a16-=i;

a17=a16 ^ a17;

a16=-;

a16=a16^a17;

if(a16==c[i]){

puts("YES");

a17=a16;

}

else{

puts("NO");

break;

}

}

return ;

}

写出相应的解密脚本:

#include

int main(){

int i;

unsigned char a16=,a17=;

unsigned char c[]={0xF4,0x0A,0xF7,0x64,0x99,0x78,0x9E,0x7D,

        0xEA,0X7B,0X9E,0X7B,0X9F,0X7E,0XEB,0X71,

        0XE8,0X00,0XE8,0X07,0X98,0X19,0XF4,0X25,

        0XF3,0X21,0XA4,0X2F,0XF4,0X2F,0XA6,0X7C};

printf("");

for(i=;i<;i++){

printf("%c",(c[i]^(-)^c[i-])+i);

}

return ;

}

UNCTF{942a4115be2359ffd675fa6338ba23b6}

WEB

checkin

右键查看源代码,查看app.03bc1faf.js,发现聊天窗提供了一些指令,尝试/flag无效果后又看到了/calc指令,可以进行数学计算,尝试了一些命令后发现fs部分命令可用,进行同步读取目录:

/calc require('fs').readdirSync('../')

发现根目录下有flag,再用readdirSync('../flag')查看目录时报错,说明flag不是目录是文件。

所以进行同步读取文件:

/calc require('fs').readFileSync('../flag'),返回了一个文件对象,重新用burp抓包可以看到对象的具体内容,ascii解码后就是flag

#!python3

flag=[,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]

for i in flag:

print(chr(i),end='')

flag{0e4d1980ef6f8a81428f83e8e1c6e22b}

MISC

信号不好我先挂了

用stegsolve打开apple.png, 选择data extract,在rgb最低位有位隐写,把它提取出后得到一个压缩包,但这个压缩包后面还有一段垃圾数据,使得无法解压,直接用binwalk强制解压,得到一张图片pen。想到了PPAP这首歌,应该是把两张图片结合,stegsolve尝试了imege_combiner的xor, and ,add 后都不行,想到盲水印也是需要两张图,用工具BlindWaterMark,得出flag:

unctf{9d0649505b702643}

快乐游戏题

亲爱的

用binwalk扫一下得知文件隐写了一个zip,zip有密码,旁边的提示写着qq音乐和一个日期,猜测是在那个时间点的一条评论。先找到李现唱的海阔天空https://c.y.qq.com/base/fcgi-bin/u?__=YTLCV4E,翻评论发现密码,解压后是一张图片,图片也有隐写内容,提取后在里面的word/media里有张图片,写着flag.

UNCTF{W3_L0v3_Unctf}

Think

这题是一个python的混淆代码,没有完全读懂,但可以看一些关键操作。解密代码是

chr((ord(__l['key'][(__l['i'] % len(__l['key']))]) ^ ord(__l['encrypted'][__l['i']].decode('base64').decode('hex')),可以看到是把后面的base64解密后和key循环异或,i是索引。据此写出代码:

#!python2

import sys

enc=['MTM=', 'MDI=', 'MDI=', 'MTM=', 'MWQ=', 'NDY=', 'NWE=', 'MDI=', 'NGQ=', 'NTI=', 'NGQ=', 'NTg=', 'NWI=', 'MTU=', 'NWU=', 'MTQ=', 'MGE=', 'NWE=', 'MTI=', 'MDA=', 'NGQ=', 'NWM=', 'MDE=', 'MTU=', 'MDc=', 'MTE=', 'MGM=', 'NTA=', 'NDY=', 'NTA=', 'MTY=', 'NWI=', 'NTI=', 'NDc=', 'MDI=', 'NDE=', 'NWU=', 'MWU=']

key='unctf'

for i in range():

sys.stdout.write(chr(ord(key\[i%\])^ord(enc\[i\].decode('base64').decode('hex'))))

flag{34a94868a8ad9ff82baadb326c513d40}

不仅仅是RSA

对公钥提取出N1, N2, E,摩斯电码用morse code reader识别出C1,C2,把N在http://www.factordb.com/ 分解,得

P1=95652716952085928904432251307911783641637100214166105912784767390061832540987

Q1=107527961531806336468215094056447603422487078704170855072884726273308088647617

P2=89485735722023752007114986095340626130070550475022132484632643785292683293897

Q2=95652716952085928904432251307911783641637100214166105912784767390061832540987

写脚本:

#!python2

import gmpy2

from Crypto.Util.number import *

from Crypto.PublicKey import RSA

f = open("pubkey1.pem", "r")

key = RSA.importKey(f.read())

n1=key.n

e1=key.e

print ('N1=',n1)

print ('E1=',e1) #然后分解N

f = open("pubkey2.pem", "r")

key = RSA.importKey(f.read())

n2=key.n

e2=key.e

print ('N2=',n2 )

print ('E2=',e2)

p1=

q1=

p2=

q2=

phi1=(p1-)*(q1-)

c1=4314251881242803343641258350847424240197348270934376293792054938860756265727535163218661012756264314717591117355736219880127534927494986120542485721347351L

d1=gmpy2.invert(e1,phi1)

m1=gmpy2.powmod(c1,d1,n1)

print hex(m1)[:].decode('hex')

phi2=(p2-)*(q2-)

c2=485162209351525800948941613977942416744737316759516157292410960531475083863663017229882430859161458909478412418639172249660818299099618143918080867132349L

d2=gmpy2.invert(e2,phi2)

m2=gmpy2.powmod(c2,d2,n2)

print hex(m2)[:].decode('hex')

UNCTF{ac01dff95336aa470e3b55d3fe43e9f6}