攻防世界 隐藏的信息

攻防世界 隐藏的信息

链接

隐藏的信息

思路

  1. 看到0开头且无>=8的数字想到八进制
  2. 计算出其10进制数,通过ascii码对应成字符
  3. base64解码

脚本

1
2
3
4
5
6
7
8
9
10
11
12
import base64

s = '0126 062 0126 0163 0142 0103 0102 0153 0142 062 065 0154 0111 0121 0157 0113 0111 0105 0132 0163 0131 0127 0143 066 0111 0105 0154 0124 0121 060 0116 067 0124 0152 0102 0146 0115 0107 065 0154 0130 062 0116 0150 0142 0154 071 0172 0144 0104 0102 0167 0130 063 0153 0167 0144 0130 060 0113'
temp = s.split(' ')
res = []
s = ''
for i in temp:
if i:
res.append(int(i, 8))
for i in res:
s += chr(i)
print(base64.b64decode(s))

==b’Well done!\n\n Flag: ISCC{N0_0ne_can_st0p_y0u}\n’==

攻防世界 3-11

攻防世界 3-11

链接

3-11

思路

  1. png图片binwalk查看一下 并没有能分离的文件

  2. zsteg查看一下隐写,发现有一个zip文件

  3. 提取zip文件,flag.txt内部是一个base64编码的字符串

  4. 解码发现是png文件

  5. 用010文件新建一个png文件复制base64解码的内容,得到flag图片

攻防世界 签到题

攻防世界 签到题

链接

签到题

思路

得到一个明显是base64的字符串,解密,获得一个奇怪的字符串。由于之前做过知道是栅栏加密+凯撒加密。解密得到flag:ssctf{ssCtf_seC10ver#@rabit}

脚本

站内搜索

攻防世界 2-1

攻防世界 2-1

链接

2-1

思路

下载png文件,查看文件头,发现错误,更改为png文件头:==89 50 4E 47==。运行模板crc32不正确,观察发现宽度为0,进行宽度爆破。

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import struct
import binascii
import bitstring


img = open("CRC32\\148a3ba22b8541f48f354f3e27f0aa4c.png","rb").read()
byte1 = bitstring.BitArray(img).bytes
# print(byte1[12:33])
chunck_lenth = byte1[11]
# print(byte1[12:12+4+chunck_lenth])
# print(bitstring.Bits(byte1[29:33]))
checksum = bitstring.Bits(byte1[29:33]).uint
for i in range(0xffff):
temp = byte1[12:16] + struct.pack(">I", i) + byte1[20:29]
crc = binascii.crc32(temp)
# crcbits = bitstring.BitArray(bin(crc)).hex
if crc == checksum:
print(hex(i))

宽度为0x2c5

修改可见png图片,flag:wdflag{Png_C2c_u_kn0W}

凯撒加密/解密

凯撒加密

原理

凯撒密码最早由古罗马军事统帅盖乌斯·尤利乌斯·凯撒在军队中用来传递加密信息,故称凯撒密码。这是一种位移加密方式,只对26个字母进行位移替换加密,规则简单,容易破解。

明文字母表 Z A B C D E F G H I J K L M N O P Q R S T U V W X Y
密文字母表 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

将明文字母表向后移动1位,A变成了B,B变成了C……,Z变成了A。同理,若将明文字母表向后移动3位:

明文字母表 X Y Z A B C D E F G H I J K L M N O P Q R S T U V W
密文字母表 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

则A变成了D,B变成了E……,Z变成了C。

脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 凯撒加密
plain = r'zz'

for i in range(26):
temp = ''
for j in plain:
if ord(j) > 96 and ord(j) < 123:
j = chr(((ord(j) - 97) + i) % 26 + 97)
temp += j
elif ord(j) < 91 and ord(j) > 64:
j = chr(((ord(j) - 65) + i) % 26 + 65)
temp += j
else:
temp += j
continue
print(temp)

栅栏加密/解密

栅栏加密

原理

将所谓栅栏密码,就是把要加密的明文分成N个一组,然后把每组的第1个字连起来,形成一段无规律的话。

例子

明文:THE LONGEST DAY MUST HAVE AN END

加密:

1、把将要传递的信息中的字母交替排成上下两行。

T E O G S D Y U T A E N N

H L N E T A M S H V A E D

2、 密文:

将下面一行字母排在上面一行的后边。

TEOGSDYUTAENN HLNETAMSHVAED

解密:

先将密文分为两行

T E O G S D Y U T A E N N

H L N E T A M S H V A E D

再按上下上下的顺序组合成一句话

明文:THE LONGEST DAY MUST HAVE AN END

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
# 栅栏加密
def crypto():
plain = input("输入明文:")
n = eval(input("输入分组字数:"))
res = ''
for i in range(n):
for j in range(int(len(plain) / n + 1)):
try:
res += plain[j * n + i]
except:
pass
print(res)


def decrypto():
plain = input('输入密文:')
for n in range(2, len(plain)-1):
res = ''
for i in range(n):
for j in range(int(plain.__len__() / n + 1)):
try:
res += plain[j * n + i]
except:
pass
print(res)

zip文件及其加密

ZIP

zip文件格式

一个 ZIP 文件由三个部分组成:

压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志
查看详情

压缩文件数据区

50 4B 03 04:头文件标记

14 00:需要pkware版本

09 00:全局方式位标记

4B 77:最后修改时间

94 4A:最后修改日期

94 4A:最后修改日期

F0 0C 59 FB:CRC-32循环校验

88 00 00 00:压缩后尺寸

B4 00 00 00:未压缩尺寸 单位字节

08 00:文件名长度

00 00:扩展记录名长度

50 4B 07 08:数据描述头标记。用于标识该文件压缩结束,该结构只有在相应的local file header中通用标记字段的第3bit设为1时才会出现,紧接在压缩文件源数据后。

F0 0C 59 FB:第一个文件的crc-32校验码

88 00 00 00:压缩后尺寸

B4 00 00 00:未压缩尺寸 单位字节

压缩文件目录区

50 4B 01 02:目录中文件文件头标记

1F 00:压缩使用pkware版本

14 00:解压文件所需pkware最低版本

09 00:全局方式位标记(有无加密 伪加密可以改成09 00,无加密为00 00)

08 00:压缩方式

4B 77:修改文件时间

94 4A:修改文件日期

F0 0C 59 FB:crc-32校验码

88 00 00 00:压缩后尺寸

B4 00 00 00:未压缩尺寸 单位字节

08 00:文件名长度

24 00:扩展字段长度

00 00:文件注释长度

00 00:磁盘开始号

00 00:内部文件属性

20 00 00 00:文件外部属性

00 00 00 00:局部头部偏移量

压缩源文件目录结束标志

50 4B 05 06:局部头部偏移量

00 00:当前磁盘号

00 00:开始磁盘号

02 00:本磁盘记录总数

02 00:目录区中记录总数

B6 00 00 00:目录区大小

65 01 00 00:目录区对第一张磁盘偏移量

1F 00:注释长度

·· ··:注释

ZIP加密/解密

伪加密

将原文件目录区的全局方式标记为第二个数字改为偶数即为加密,改为奇数则为未加密。

明文攻击

如果得到了加密压缩包中的某个文件,那么就可以通过明文攻击来获取压缩密码。

例子:压缩readme.txt发现crc32与加密压缩包中一样使用archpr进行明文爆破

如果出现以下错误,那么一般就是你使用的压缩软件或是压缩算法和待破解的压缩包所使用的软件不同,可以用WinRAR,7z,2345好压等都试一下

在选定的档案中没有匹配的文件。如果您想要仅使用文件的一部分执行明文攻击,请修改档案,使每个档案中只包含一个文件。

bitstring 库使用

位,字节,字

​ 位、字节、字是计算机数据存储的基本单位,每一个位存储一个1位的二进制编码(即0或1),一个字节由八位组成。字是代表计算机处理指令或数据的二进制位数,32位系统1字=32位,64位系统1字=64位

bitstring 库

bitstring是一个python的库,可以使得对二进制数据的操作变得简单。

下载

https://bitstring.readthedocs.io/en/latest/

创建bitArray

1
2
a = bitArray('0xff91')
b = bitArray('0b110')

0x是十六进制前缀,0b是二进制前缀。文档中说其未成bytes会内部处理。若输入的不是字符串会创造输入整数位的空比特串。

使用内部转换

1
2
3
4
5
6
7
8
a.bin
'1111111100000001'
b.oct
'6'
b.int
-2
a.bytes
b'\xff\x01'

这些属性是在执行时开始计算,bin以及oct返回的是字符串。并且需要oct原比特串满足是3位的倍数。而hex需要是4位。bytes属性可以返回python3的bytes对象。

比特串填充

1
2
3
4
(b + [0]).hex
'c'
([0] + b).hex
'6'

零位[0]也可以写成BitArray([0]), BitArray([0]), BitArray('0b0'), BitArray(bin='0'),'0b0' 可以是1

修改比特串

bitArray可以将其视为位列表,使用索引可以对其进行切片,删除,插入。

删除
1
2
3
4
5
print(a[3:9])
0b111110
del a[-6:]
print(a)
0b1111111100

这里的del是删除列表中的a[-1]~a[-6]对象,解除了对原数据的引用。不过我想既然引用计数已经为0,数据占用的空间应该也被释放了。

切片

单个的元素会返回一个bool值

添加
1
2
3
a.prepend('0b01')
a.append('0o7')
a += '0x06'
查找
1
2
3
4
5
a = BitArray('0xa9f')
a.find('0x4f')
(3,)
a == '0b101, 0x4f, 0b1'
True

构造比特串

sequence_header() No. of bits Mnemonic
sequence_header_code 32 bslbf
horizontal_size_value 12 uimsbf
vertical_size_value 12 uimsbf
aspect_ratio_information 4 uimsbf
frame_rate_code 4 uimsbf
bit_rate_value 18 uimsbf
marker_bit 1 bslbf
vbv_buffer_size_value 10 uimsbf
constrained_parameters_flag 1 bslbf
load_intra_quantiser_matrix 1 uimsbf
if (load_intra_quantiser_matrix)
{ intra_quantiser_matrix[64] } 8*64 uimsbf
load_non_intra_quantiser_matrix 1 uimsbf
if (load_non_intra_quantiser_matrix)
{ non_intra_quantiser_matrix[64] } 8*64 uimsbf
next_start_code()
1
2
3
4
5
6
7
8
9
10
s = BitArray()
s.append('0x000001b3') # the sequence_header_code
s.append('uint:12=352') # 12 bit unsigned integer
s.append('uint:12=288')
...
# s = BitArray('0x000001b3, uint:12=352, uint:12=288')
'''
width, height = 352, 288
s = bitstring.pack('0x000001b3, 2*uint:12', width, height)
'''

其中pack函数可以用字典作为第二个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fmt = 'sequence_header_code,
uint:12=horizontal_size_value,
uint:12=vertical_size_value,
uint:4=aspect_ratio_information,
...
'
d = {'sequence_header_code': '0x000001b3',
'horizontal_size_value': 352,
'vertical_size_value': 288,
'aspect_ratio_information': 1,
...
}

s = bitstring.pack(fmt, **d)

pack 返回的是bit流

操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> s
BitStream('0x000001b31601201')
>>> s.pos
0
>>> s.read(24)
BitStream('0x000001')
>>> s.pos
24
>>> s.read('hex:8')
'b3'
>>> s.pos
32
>>> s.readlist('2*uint:12')
[352, 288]
>>> s.unpack('bytes:4, 2*uint:12, uint:4')
['\x00\x00\x01\xb3', 352, 288, 1]

bitStream的操作有类似游标的位移,unpack是从比特串开头开始。

创建

bitstrings库一共提供了四个类,Bits,BitArray,BitStream,ConstBitStream。

使用类的情况

  • 如果要改变比特串的内容,使用bitArray或者bitSteam
  • 如果要作为字典中的key使用,那么使用bits或者constBitSteam
  • 如果直接从文件中读取比特流,bitArray 或者bitSteam会将整个文件读入内存,而bits或者constBitStream不会。
  • 内存效率最高的类是bits。

从原始字节数据创建

1
2
a = BitArray(bytes=b'\x00\x01\x02\xff', length=28, offset=1)
b = BitArray(bytes=open("somefile", 'rb').read())

lenth的单位是bits,offset同理偏移量截断开头。

包装

创建bitStream对象可以用pack函数,

bitstring.pack(format, *values, **kwargs)

例如:

1
2
3
s = bitstring.pack('hex:32, uint:12, uint:12', '0x000001b3', 352, 288)
# 相当于
s = BitStream('0x0000001b3, uint:12=352, uint:12=288')

这个可以用函数实现更通用的创建

1
2
3
4
5
def foo(a, b, c, d):
return bitstring.pack('uint:8, 0b110, int:6, bin, bits', a, b, c, d)

s1 = foo(12, 5, '0b00000', '')
s2 = foo(101, 3, '0b11011', s1)

切片,索引,拼接。

切片
1
2
3
4
>>> a = BitArray('0b00011110')
>>> b = a[3:7]
>>> print(a, b)
0x1e 0xf
索引

单个位索引返回一个布尔值,len属性返比特串长度

拼接
  • append和prepend
  • join拼接比特串
  • del删除
  • insert在bitarray使用时需要指定pos,bitstream会在当前pos进行insert。
  • overwrite和insert大致相同,但是会覆盖。
使用函数 使用切片
s.insert(bs, pos) s[pos:pos] = bs
s.overwrite(bs, pos) s[pos:pos + bs.len] = bs
s.append(bs) s[s.len:s.len] = bs
s.prepend(bs) s[0:0] = bs`
分割
1
2
3
4
5
6
7
>>> a = BitArray('0x4700004711472222')
>>> for s in a.split('0x47', bytealigned=True):
... print(s.hex)

470000
4711
472222

spilit返回迭代器

1
2
3
4
5
6
7
>>> a = BitArray('0x47001243')
>>> for byte in a.cut(8):
... print(byte.hex)
47
00
12
43

cut可以分成相等的部分。

读取和解析

每一个比特流都有一个pos属性,长度范围为0到最后一位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> s = ConstBitStream(filename='test/test.m1v')
>>> print(s.pos)
0
>>> start_code = s.read(32).hex
>>> width = s.read(12).uint
>>> height = s.read(12).uint
>>> print(start_code, width, height, s.pos)
000001b3 352 288 56
>>> s.pos += 37
>>> flags = s.read(2)
>>> constrained_parameters_flag = flags.read(1)
>>> load_intra_quantiser_matrix = flags.read(1)
>>> print(s.pos, flags.pos)
95 2

如果一次读取多个数据可以用readlist

1
a, b, c = s.readlist([32, 8, 24])

peek方法不会改变bitstream的pos属性。其使用和read类似。

查找等操作和字符串类似不在列举

官方文档

杂项

  1. bytealign()方法返回跳跃步数,使得pos成为8的倍数

  2. reverse将bitarray反转,可以指定范围

  3. tobytes会把bitarray转换为字节,如果不够8位在后面补0

  4. startswith / endswith判断比特串是否已某开头或结束

攻防世界 nomal_png

图片的文件格式

png图片

文件头

00000000h: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D49 48 44 52 ; 塒NG……..IHDR
00000010h: 00 00 00 CE 00 00 00 CE 08 02 00 00 00 F9 7D AA ; …?..?….鶀?
00000020h: 93 00 00 00 09 70 48 59 73 00 00 0A 75 00 00 0A ; ?…pHYs…u…
00000030h: 75 01 4A 25 DD FD 00 00 0C 91 49 44 41 54 78 9C ; u.J%蔟…慖DATx?
00000040h: ED 9D D9 96 DC 2A 0C 45 A9 AC FC FF 2F D7 7D 70 ; 頋贃?.E┈?/讅p
00000050h: C7 97 66 10 9A 98 CF 7E C8 EA 54 95 6D 86 83 24 ; 菞f.殬蟸汝T昺唭$
00000060h: 04 B6 3F DF EF 37 00 D0 9F 3F B3 0B 00 6E 01 52 ; .?唢7.袩??.n.R
00000070h: 03 83 F8 3B BB 00 AB F2 F9 98 0E 47 58 92 01 A9 ; .凐;?鶚.GX??

89 50 4E 47 0D 0A 1A 0A是PNG头部署名域,表示这是一个PNG图片
00 00 00 0D描述IHDR头部大小
49 48 44 52是Chunk Type Code, 这里Chunk Type Code=IHDR
00 00 00 CE 00 00 00 CE 08 02 00 00 00描述了Chunk Data,它是可变长度数据,00 00 00 0D 定义了长度为13个Bytes,所以,这里,你看到是13个字节)
F9 7D AA 93是对IHDR的CRC校验

查看图片,模板运行失败。crc校验码不对。进行高度爆破。结果为0x40b
CRC爆破高度脚本:

import struct
import binascii
import bitstring
1
2
3
4
5
6
7
8
9
10
11
12
13
img = open(img_path,"rb").read()
byte1 = bitstring.BitArray(img).bytes
# print(byte1[12:33])
chunck_lenth = byte1[11]
# print(byte1[12:12+4+chunck_lenth])
# print(bitstring.Bits(byte1[29:33]).uint)
checksum = bitstring.Bits(byte1[29:33]).uint
for i in range(0xffff):
temp = byte1[12:20] + struct.pack(">I", i) + byte1[24:29]
crc = binascii.crc32(temp)
# crcbits = bitstring.BitArray(bin(crc)).hex
if crc == checksum:
print(hex(i))

请我喝杯咖啡吧~

支付宝
微信