Samsung CTF Quals 2018 - Flood (431 pts.)

먼저 문제를 풀기위해서는 ONOS app (.oar) 파일을 만들어야 하는데 docker로 설치하는 방법이 문제 사이트에 친절히 설명되어있어서 좋았습니다.
https://web.archive.org/web/20180831061125/http://sdn.eatpwnnosleep.com/start (추후에 문제 사이트가 닫힐 것으로 예상하여 archive url로 대체했습니다.)

Flood 문제는 DDoS트래픽을 막으면 되는 문제라고 적혀있습니다.

하지만 문제에서 제공해준 .pcap파일을 보면 공격이 들어오는 패킷의 길이가 68로 동일하다는 것을 알 수 있습니다.

이 점을 이용해서 길이가 68이라면 트래픽을 차단하게 했습니다.

하지만 이건 당연하게도 출제자분이 의도한 풀이는 아닙니다. 좀더 패킷의 길이가 다양하거나 종류가 달랐다면 적혀있던 난이도 extreme그대로 어려웠을 것 같네요.
일단 .pcap파일에서 이상한 점은 패킷의 길이 말고도 Frame Check Sequence값도 제대로 맞지 않다는 점도 있었긴 했지만..

출제자분이 원했던 풀이는 어떻게 풀면 될지 궁금하네요.

AppComponent.java

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
* Copyright 2018-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.myapp;


import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;

import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.TCP;

import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.Path;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.net.packet.PacketService;
import org.onosproject.net.topology.TopologyService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// added
import java.nio.ByteBuffer;

@Component(immediate = true)
public class AppComponent {

private final Logger log = LoggerFactory.getLogger(getClass());

@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;

@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;

@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;

@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;

private ApplicationId appId;
private MyPacketProcessor processor = new MyPacketProcessor();

@Activate
protected void activate() {
// Print log to the ONOS screen.
log.info("Started");
appId = coreService.registerApplication("com.example.myapp");

// Make all IPv4 packets to pass through this app.
packetService.addProcessor(processor, PacketProcessor.director(2));
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.build();
packetService.requestPackets(selector, PacketPriority.REACTIVE, appId);
}

@Deactivate
protected void deactivate() {
log.info("Stopped");
if (processor != null) {
packetService.removeProcessor(processor);
processor = null;
}
}

private class MyPacketProcessor implements PacketProcessor {
// This method is invoked whenever we receive a packet
// that is not matched in the routing tables.
@Override
public void process(PacketContext context) {
// Return if another app has already dealt with this packet.
if (context.isHandled())
return;

InboundPacket pkt = context.inPacket();
Ethernet ethPkt = pkt.parsed();
if (ethPkt == null)
return;
// Forward LLDP and ARP.
if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
allowPacket(context, ethPkt);
return;
}
// Forward ICMP (ping).
IPv4 ip = (IPv4) ethPkt.getPayload();
if (ip.getProtocol() == IPv4.PROTOCOL_ICMP) {
allowPacket(context, ethPkt);
return;
}

if (ip.getProtocol() != IPv4.PROTOCOL_TCP)
return;

// begin added code
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(pkt.unparsed());
buffer.flip();
int length = buffer.remaining();

if (length == 68)
return;
// end added code

TCP tcp = (TCP)ip.getPayload();
onPacket(context, ethPkt, ip, tcp);
}

private void allowPacket(PacketContext context, Ethernet ethPkt) {
HostId dstId = HostId.hostId(ethPkt.getDestinationMAC());
Host dst = hostService.getHost(dstId);
// If we don't know where it's heading for, just broadcast it.
if (dst == null) {
if (topologyService.isBroadcastPoint(
topologyService.currentTopology(),
context.inPacket().receivedFrom())) {
context.treatmentBuilder().setOutput(PortNumber.FLOOD);
context.send();
}
else {
context.block();
}
}
// Forward packet to its destination.
else {
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(dst.location().port())
.build();
OutboundPacket packet = new DefaultOutboundPacket(
dst.location().deviceId(),
treatment,
context.inPacket().unparsed());
packetService.emit(packet);
}
}

private String ipToString(int ip) {
return String.format("%d.%d.%d.%d",
(ip >> 24 & 0xff), (ip >> 16 & 0xff), (ip >> 8 & 0xff), (ip & 0xff));
}

private void dumpPacket(IPv4 ip, TCP tcp) {
String flagsString = "";
short flags = tcp.getFlags();
if ((flags & (1 << 0)) != 0) flagsString += " FIN";
if ((flags & (1 << 1)) != 0) flagsString += " SYN";
if ((flags & (1 << 2)) != 0) flagsString += " RST";
if ((flags & (1 << 3)) != 0) flagsString += " PSH";
if ((flags & (1 << 4)) != 0) flagsString += " ACK";

log.info(String.format("Got %s:%d -> %s:%d seq=%08x ack=%08x flags=%s",
ipToString(ip.getSourceAddress()), tcp.getSourcePort(),
ipToString(ip.getDestinationAddress()), tcp.getDestinationPort(),
tcp.getSequence(), tcp.getAcknowledge(), flagsString));
}

private void onPacket(PacketContext context, Ethernet ethPkt, IPv4 ip, TCP tcp) {
dumpPacket(ip, tcp);
allowPacket(context, ethPkt);
}
}
}
Share