CTFSHOW: 36D杯 misc ez-qrcode

发布于:2023-01-18 ⋅ 阅读:(541) ⋅ 点赞:(0)


前言

本题为:CTFSHOW的36D杯 misc ez-qrcode
和攻防世界的qr-easy解法基本一模一样。

1.第一眼

在这里插入图片描述

用ps稍微处理一下:

在这里插入图片描述

此二维码的大小为 29x29,版本V的大小为N × NN = 17 + 4V,所以这是版本 3。

2.格式信息

在这里插入图片描述

图中两处红色方框,表示二维码的格式信息,前面7位,后面8位。

后面8位的格式信息为:11100111,前7位我们不清楚,所以我们的信息字符串为:???????11100111

所有格式信息字符串的列表

ECC 级别 掩码图案 类型信息比特
L 0 111011111000100
L 1 111001011110011
L 2 111110110101010
L 3 111100010011101
L 4 110011000101111
L 5 110001100011000
L 6 110110001000001
L 7 110100101110110
M 0 101010000010010
M 1 101000100100101
M 2 101111001111100
M 3 101101101001011
M 4 100010111111001
M 5 100000011001110
M 6 100111110010111
M 7 100101010100000
Q 0 011010101011111
Q 1 011000001101000
Q 2 011111100110001
Q 3 011101000000110
Q 4 010010010110100
Q 5 010000110000011
Q 6 010111011011010
Q 7 010101111101101
H 0 001011010001001
H 1 001001110111110
H 2 001110011100111
H 3 001100111010000
H 4 000011101100010
H 5 000001001010101
H 6 000110100001100
H 7 000100000111011

根据二维的信息字符串???????11100111再结合所有格式信息字符串的列表或者上图表格。

很容易得到这个二维码的ECC级别为H和掩码模式为2。

3.去掉掩码

掩码号 如果下面的公式对于给定的行/列坐标为真,则切换该坐标处的位
0 (行 + 列) mod 2 == 0
1 (行) mod 2 == 0
2 (列) mod 3 == 0
3 (行 + 列) mod 3 == 0
4 (floor(行 / 2) + floor(列 / 3)) mod 2 == 0
5 ((行 * 列) mod 2) + ((行 * 列) mod 3) == 0
6 ( ((行 * 列) mod 2) + ((行 * 列) mod 3) ) mod 2 == 0
7 ( ((行 + 列) mod 2) + ((行 * 列) mod 3) ) mod 2 == 0

根据QR Mask Patterns Explained,也就是上面的表格,2 号掩码有公式(列) mod 3 == 0。注意列号是从0开始的,所以我们要切换坐标为0,3,6,9, … ,27的列的位。

二维码也有固定的图案,所以我们只需要切换数据部分的位即可。查看数据区和位顺序。

在这里插入图片描述
(通过wikipedia.org)

所以,原始的 D1-D26 是:

D1 = 00010100
D14 = 10100010
D2 = 00110011
D15 = 00010001
D3 = 00110011
D16 = 01110110
D4 = 11000110
D17 = 01010111
D5 = 00010110
D18 = 01000100
D6 = 11011101
D19 = 00101100
D7 = 00011111
D20 = 01011110
D8 = 00011100
D21 = 00000010
D9 = 00100001
D22 = 01100110
D10 = 00110110
D23 = 01010111
D11 = 00010110
D24 = 11010000
D12 = 11100110
D25 = 11101101
D13 = 10110011
D26 = 01000100

去掉掩码后,(列) mod 3 == 0

D1 = 01000001
D14 = 11110111
D2 = 01100110
D15 = 01000100
D3 = 01100110
D16 = 01110110
D4 = 11000110
D17 = 01010111
D5 = 00010110
D18 = 01000100
D6 = 01110111
D19 = 10000110
D7 = 10110101
D20 = 11110111
D8 = 01000110
D21 = 01010111
D9 = 01110100
D22 = 00110110
D10 = 00110110
D23 = 01010111
D11 = 00010110
D24 = 11010000
D12 = 11100110
D25 = 11101100
D13 = 11100110
D26 = 00010001

4.数据解码

解码有模式指示符

  • 0001:数字模式(每 3 位 10 位)
  • 0010:字母数字模式(每 2 个字符 11 位)
  • 0100:字节模式(每个字符 8 位)
  • 1000:汉字模式(每个字符 13 位)
  • 0111: ECI 模式

字符计数指示符跟在模式指示符之后。

  • 版本 1-9
    • 数字模式:10 位
    • 字母数字模式:9位
    • 字节模式:8位
    • 汉字模式:8位
  • 版本 10–26
    • 数字模式:12位
    • 字母数字模式:11 位
    • 字节模式:16位
    • 汉字模式:10位
  • 版本 27–40
    • 数字模式:14位
    • 字母数字模式:13 位
    • 字节模式:16位
    • 汉字模式:12位

查看每种模式的编码过程:

让我们从上面的数据 D1-D26 开始:

data = '01000001' \
       '01100110' \
       '01100110' \
       '11000110' \
       '00010110' \
       '01110111' \
       '10110101' \
       '01000110' \
       '01110100' \
       '00110110' \
       '00010110' \
       '11100110' \
       '11100110' \
       '11110111' \
       '01000100' \
       '01110110' \
       '01010111' \
       '01000100' \
       '10000110' \
       '11110111' \
       '01010111' \
       '00110110' \
       '01010111' \
       '11010000' \
       '11101100' \
       '00010001'
alphanumeric = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'.chars

def read(str, size)
  str.slice!(0, size)
end

def kanji(num)
  if num >= 0x1740
    (0xC140 + num / 0xC0 * 0x100 + num % 0xC0)
      .chr(Encoding::Shift_JIS).encode(Encoding::UTF_8)
  else
    (0x8140 + num / 0xC0 * 0x100 + num % 0xC0)
      .chr(Encoding::Shift_JIS).encode(Encoding::UTF_8)
  end
end

loop do
  case mode = read(data, 4)
  when '0010' # Alphanumeric
    count = read(data, 9).to_i(2)
    (count / 2).times do
      chunk = read(data, 11).to_i(2)
      print alphanumeric[chunk / 45] + alphanumeric[chunk % 45]
    end
    print alphanumeric[read(data, 11).to_i(2)] if count.odd?
  when '0100' # Byte
    count = read(data, 8).to_i(2)
    count.times do
      print read(data, 8).to_i(2).chr
    end
  when '1000' # Kanji
    count = read(data, 8).to_i(2)
    count.times do
      print kanji(read(data, 13).to_i(2))
    end
  when '0000' # Terminate
    break
  else
    fail "Unhandled mode #{mode}"
  end
end

Ruby在线网站运行即可得到

flag{TgCannotGetHouse}


Python代码同样也没有问题:

# D1-D26:
data = ['01000001', '01100110', '01100110', '11000110', '00010110', '01110111', '10110101', '01000110', '01110100', '00110110', '00010110', '11100110', '11100110', '11110111', '01000100', '01110110', '01010111', '01000100', '10000110', '11110111', '01010111', '00110110', '01010111', '11010000', '11101100', '00010001']

data = "".join(data)
data = data[4:]

for i in range(0, len(data), 8):
	print(chr(int(data[i:i+8], 2)), end="")

运行效果如下:

在这里插入图片描述

flag{TgCannotGetHouse}

拜拜!

5.参考网站:

https://yous.be/2014/12/07/seccon-ctf-2014-qr-easy-write-up/

https://www.zhihu.com/question/65253283

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到