old_driver

点击此处获得更好的阅读体验


WriteUp来源

https://mp.weixin.qq.com/s/76bk1oYsnrL47OesV09P_A

题目描述

题目考点

  • 对抗样本及防御

解题思路

本题考查选手对对抗样本及相关防御技术的了解。

官方WP

题目中生成对抗样本使用了经典的C&W攻击,且kappa很小,因此对抗样本会分布在两个分类的决策边界上,可以根据classification score来找分布在决策边界附近的样本从而找到对抗样本。此外,也有很多其他解法可以使用,比如重新训练一个新的模型来检测;或是使用常规的对抗样本防御方法(如对输入加filter)对使用防御前后的output进行比较。

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
from glob import glob
import torch
from torch import nn, optim
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torchattacks # https://github.com/Harry24k/adversarial-attacks-pytorch
from torchvision import transforms
from PIL import Image
import random
import os
from hashlib import md5, sha256
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.conv1 = nn.Conv2d(3, 100, 5)
self.conv1_bn = nn.BatchNorm2d(100)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(100, 150, 3)
self.conv2_bn = nn.BatchNorm2d(150)
self.conv3 = nn.Conv2d(150, 250, 1)
self.conv3_bn = nn.BatchNorm2d(250)
self.fc1 = nn.Linear(250 * 3 * 3, 350)
self.fc1_bn = nn.BatchNorm1d(350)
self.fc2 = nn.Linear(350, 10)
self.dropout = nn.Dropout(p=0.5)

def forward(self, x):
x = self.pool(F.elu(self.conv1(x)))
x = self.dropout(self.conv1_bn(x))
x = self.pool(F.elu(self.conv2(x)))
x = self.dropout(self.conv2_bn(x))
x = self.pool(F.elu(self.conv3(x)))
x = self.dropout(self.conv3_bn(x))
x = x.view(-1, 250 * 3 * 3)
x = F.elu(self.fc1(x))
x = self.dropout(self.fc1_bn(x))
x = self.fc2(x)
return x
# load pretrained model
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
transform = transforms.ToTensor()
model = Model().to(device)
check_point = torch.load('model.pt', map_location=device)
model.load_state_dict(check_point)
model.eval()
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
dataset = []
images = glob('imgs/1/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 1))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[1].permute(1, 2, 0))
1
2
3
4
5
6
7
8
9
<ipython-input-4-d76776ea6971>:17: UserWarning: This overload of nonzero is deprecated:
nonzero(Tensor input, *, Tensor out)
Consider using one of the following signatures instead:
nonzero(Tensor input, *, bool as_tuple) (Triggered internally at /tmp/pip-req-build-as628lz5/torch/csrc/utils/python_arg_parser.cpp:766.)
candidates = torch.nonzero((diff<2) * (labels[:,0] == 1))

19 [[10.330801963806152, 8.862082481384277]] [[1, 0]]
128 [[14.271539688110352, 14.26427936553955]] [[1, 9]]
171 [[10.529718399047852, 9.856830596923828]] [[1, 0]]

对照benign样本,很容易发现128,171为对抗样本,符合hint1中的0->1, 9->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
dataset = []
images = glob('imgs/0/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 0))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[0].permute(1, 2, 0))
1
2
3
34 [[10.157703399658203, 10.141921997070312]] [0, 1]]
99 [[6.645545959472656, 5.240039348602295]] [0, 3]]
212 [[7.364688873291016, 5.5696306228637695]] [[0, 3]]

对照benign样本,很容易发现34为对抗样本,符合hint1中的1->0

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
dataset = []
images = glob('imgs/6/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 6))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[0].permute(1, 2, 0))
1
2
3
4
5
6
7
8 [[3.92341685295105, 1.930528998374939]] [[6, ]]
44 [[13.969378471374512, 12.090963363647461]] [6, 5]]
167 [[8.315347671508789, 7.1767144203186035]] [6, 0]]
214 [[6.206953048706055, 5.905989646911621]] [6, 2]]
284 [[4.233434200286865, 4.125830173492432]] [6, 0]]
302 [[3.7329397201538086, 2.3108153343200684]] [6, 7]]
391 [[18.115943908691406, 18.073213577270508]] [[6, 5]]

对照benign样本,很容易发现214,391为对抗样本,符合hint1中的2->6, 5->6

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
dataset = []
images = glob('imgs/4/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 4))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[0].permute(1, 2, 0))
1
2
3
4
5
6
7
8
9
10
11
12
36 [[6.250286102294922, 6.000677108764648]] [4, 0]]
50 [[9.46463394165039, 7.811725616455078]] [4, 0]]
61 [[14.604730606079102, 13.94554328918457]] [4, 3]]
76 [[7.9692583084106445, 6.7764787673950195]] [4, 0]]
80 [[8.397534370422363, 8.131750106811523]] [4, 0]]
149 [[16.59321403503418, 16.373197555541992]] [4, 3]]
180 [[10.952032089233398, 10.29800033569336]] [4, 3]]
201 [[6.7305755615234375, 6.548830509185791]] [4, 0]]
257 [[7.356083869934082, 6.452930450439453]] [4, 0]]
273 [[15.939336776733398, 15.877418518066406]] [4, 3]]
277 [[16.802980422973633, 15.91077995300293]] [4, 3]]
352 [[7.009323596954346, 6.297818183898926]] [[4, 0]]

对照benign样本,很容易发现273为对抗样本,符合hint1中的3->4

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
dataset = []
images = glob('imgs/3/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 3))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[0].permute(1, 2, 0))
1
116 [[19.1494140625, 19.01301383972168]] [[3, 4]]

对照benign样本,很容易发现116为对抗样本,符合hint1中的4->3

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
dataset = []
images = glob('imgs/5/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 5))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[0].permute(1, 2, 0))
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
7 [[9.436586380004883, 8.46451187133789]] [[5, 6]]
10 [[10.523250579833984, 8.977357864379883]] [[5, 6]]
16 [[11.44473648071289, 11.173833847045898]] [[5, 6]]
20 [[8.587878227233887, 8.520938873291016]] [[5, 6]]
21 [[9.643725395202637, 7.751104354858398]] [[5, 6]]
24 [[12.76188850402832, 12.530357360839844]] [[5, 6]]
31 [[14.857566833496094, 14.418084144592285]] [[5, 6]]
46 [[13.962096214294434, 13.051092147827148]] [[5, 6]]
53 [[10.21031379699707, 10.173200607299805]] [[5, 6]]
68 [[13.734416961669922, 11.935338973999023]] [[5, 6]]
79 [[7.737946510314941, 6.343132972717285]] [[5, 6]]
100 [[15.554790496826172, 14.059501647949219]] [[5, 6]]
101 [[14.029050827026367, 13.515676498413086]] [[5, 6]]
112 [[10.697875022888184, 9.460480690002441]] [[5, 6]]
119 [[8.557901382446289, 6.617669582366943]] [[5, 0]]
120 [[13.066328048706055, 12.191879272460938]] [[5, 6]]
124 [[8.667509078979492, 7.228110313415527]] [[5, 6]]
130 [[14.435691833496094, 14.335365295410156]] [[5, 6]]
139 [[9.271245002746582, 9.001958847045898]] [[5, 6]]
146 [[14.412433624267578, 13.142959594726562]] [[5, 6]]
219 [[14.200758934020996, 12.73554801940918]] [[5, 6]]
230 [[7.614727020263672, 6.99611759185791]] [[5, 6]]
234 [[14.790428161621094, 13.05992317199707]] [[5, 6]]
239 [[9.650760650634766, 8.966289520263672]] [[5, 6]]
241 [[11.708353042602539, 11.589008331298828]] [[5, 6]]
248 [[12.060556411743164, 10.089497566223145]] [[5, 6]]
256 [[9.723258972167969, 8.10781478881836]] [[5, 6]]
293 [[16.74040412902832, 16.495595932006836]] [[5, 6]]
327 [[15.104751586914062, 13.598688125610352]] [[5, 6]]
342 [[9.077428817749023, 8.786874771118164]] [[5, 6]]
351 [[13.759869575500488, 11.78742504119873]] [[5, 6]]
367 [[14.54043960571289, 12.767509460449219]] [[5, 6]]

由于5,6两个class相似度较高,因此classification score也比较相近,仔细观察一下可以发现293是对抗样本

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
dataset = []
images = glob('imgs/8/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 8))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[0].permute(1, 2, 0))
1
2
3
4
5
6
7
8
9
99 [[11.640213012695312, 9.784014701843262]] [[8, 9]]
127 [[12.068662643432617, 12.007749557495117]] [[8, 7]]
166 [[9.30318546295166, 8.16383171081543]] [[8, 7]]
233 [[5.622105121612549, 4.620823860168457]] [[8, 9]]
269 [[3.499361515045166, 2.3027079105377197]] [[8, 1]]
322 [[10.474091529846191, 9.613511085510254]] [[8, 9]]
342 [[9.152402877807617, 7.786888599395752]] [[8, 7]]
356 [[6.692662239074707, 4.8316850662231445]] [[8, 9]]
372 [[5.5466790199279785, 4.865174293518066]] [[8, 9]]

对照benign样本,很容易发现127为对抗样本,符合hint1中的7->8

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
dataset = []
images = glob('imgs/7/*.png')
images.sort(key=lambda fname: int(fname[7:-4]))
for fname in images:
dataset.append((transform(Image.open(fname)).to(device), torch.tensor(0, device=device)))

dl = DataLoader(dataset, batch_size=64, shuffle=False)

plt.figure(figsize=(16, 16))
cnt = 1
with torch.no_grad():
for idx, xy in enumerate(dl):
x, y = xy
output = model(x)
topk, labels = torch.topk(output, k=2, dim=1)
diff = topk[:,0] - topk[:,1]
candidates = torch.nonzero((diff<2) * (labels[:,0] == 7))
for candidate in candidates:
print((candidate + idx*64).item(), topk[candidate].tolist(), labels[candidate].tolist())
plt.subplot(8, 8, cnt)
cnt += 1
plt.axis('off')
plt.title('idx: {}'.format((candidate + idx*64).item()))
plt.imshow(x[candidate[0]].permute(1, 2, 0))
plt.subplot(8, 8, cnt-cnt%8+9)
plt.axis('off')
plt.title('benign')
plt.imshow(x[0].permute(1, 2, 0))
1
59 [[6.843816757202148, 6.5867600440979]] [[7, 8]]

对照benign样本,很容易发现59为对抗样本,符合hint1中的8->7

最后计算flag

1
2
3
4
5
adversarial_images = [128, 171, 34, 214, 391, 273, 116, 293, 127, 59]
flag = 'flag{' + md5(str(sorted(adversarial_images)).encode()).hexdigest() + '}'
hint2 = sha256(str(sorted(adversarial_images)).encode()).hexdigest()
print('flag:', flag)
print('hint2:', hint2)

大佬WP by NanoApe

看题目是要你找出所有对抗样本,那众所周知对抗样本的改动肯定很小,所以直接人眼遍历全部图片,找不同,就可以了。

1
2
3
4
5
6
from hashlib import md5, sha256
for i in range(0, 749):
a = [128, 171, 116, 273, 293, 214, 391, 59, 127, 34]
if (sha256(str(sorted(a)).encode()).hexdigest() =='502e0423b82c251f280e4c3261ee4d01dca4f6fe0b663817e9fd43dffefc5ce9'):
print(md5(str(sorted(a)).encode()).hexdigest())
break

大佬WP by Ha1c9on

分析 attack.py 可知 代码基本就是找出样本攻击中错误的图⽚序号,然后带到adversarial_images = [171,34,214,273,116,391,293,127,59,128]数组,加密下就是flag。出题⼈还贴⼼的给了哪个⾥⾯有攻击图⽚,以及flag验证的sha1。

众所周知,这种题⽤时间排序就可以秒⼀切。

只需要按修改时间排序,就可以拿到错误的图⽚啦。找不到的⼿动找⼀下就好了!找到如下数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from glob import glob
import torch
from torch import nn, optim
import torch.nn.functional as F
import torchattacks # https://github.com/Harry24k/adversarial-attacks-pytorch
from torchvision import transforms
from PIL import Image
import random
import os
from hashlib import md5, sha256
adversarial_images = [171,34,214,273,116,391,293,127,59,128]
hint2 = sha256(str(sorted(adversarial_images)).encode()).hexdigest()
flag = 'flag{' + md5(str(sorted(adversarial_images)).encode()).hexdigest() + '}'
print(flag)

Flag

1
flag{00d531934c171e1a71405f5e1f171d65}