resend SYN+ACK with normal TTL

This commit is contained in:
Christian Ulrich 2020-08-23 14:36:19 +02:00
parent 0f622c0953
commit acfcb79108
No known key found for this signature in database
GPG Key ID: 8241BE099775A097
2 changed files with 64 additions and 36 deletions

View File

@ -58,10 +58,11 @@ type
URG
IpPacket* = object
ipAddrSrc*: IpAddress
ipAddrDst*: IpAddress
ipTTL*: uint8
case protocol*: Protocol
of tcp:
tcpIpSrc*: IpAddress
tcpIpDst*: IpAddress
tcpPortSrc*: Port
tcpPortDst*: Port
tcpSeqNumber*: uint32
@ -87,8 +88,9 @@ proc parseEthernetPacket*(input: string): IpPacket =
let tcpHeader = cast[ptr Tcphdr](cast[int](ipHeader) + ipHeader.ip_hl.int * 4)
result = IpPacket(protocol: tcp,
tcpIpSrc: ipSrc,
tcpIpDst: ipDst,
ipAddrSrc: ipSrc,
ipAddrDst: ipDst,
ipTTL: ipHeader.ip_ttl,
tcpPortSrc: Port(ntohs(tcpHeader.th_sport)),
tcpPortDst: Port(ntohs(tcpHeader.th_dport)),
tcpSeqNumber: ntohl(tcpHeader.th_seq),
@ -132,8 +134,8 @@ proc serialize*(packet: IpPacket): string =
of tcp:
result = newString(sizeof(Ip) + sizeof(Tcphdr))
zeroMem(result.cstring, result.len)
let srcIp = InAddr(s_addr: cast[uint32](packet.tcpIpSrc.address_v4))
let dstIp = InAddr(s_addr: cast[uint32](packet.tcpIpDst.address_v4))
let srcIp = InAddr(s_addr: cast[uint32](packet.ipAddrSrc.address_v4))
let dstIp = InAddr(s_addr: cast[uint32](packet.ipAddrDst.address_v4))
let ipHeader = cast[ptr Ip](addr result[0])
ipHeader.ip_hl = 5
@ -142,7 +144,7 @@ proc serialize*(packet: IpPacket): string =
ipHeader.ip_len = htons(sizeof(Ip).uint16 + sizeof(TcpHdr).uint16)
ipHeader.ip_id = htons(54321) # FIXME = random number
ipHeader.ip_off = 0
ipHeader.ip_ttl = 64
ipHeader.ip_ttl = packet.ipTTL
ipHeader.ip_p = 6.cuchar
ipHeader.ip_src = srcIp
ipHeader.ip_dst = dstIp

View File

@ -45,6 +45,20 @@ proc iptablesDelete(chain: string, rule: string) {.async.} =
let firewall_cmd = fmt"iptables -D {chain} {rule}"
discard await asyncExecCmd(firewall_cmd)
proc injectTcpPacket(rawFd: AsyncFD, ipPacket: IpPacket) {.async.} =
assert(ipPacket.protocol == tcp)
try:
let packet = serialize(ipPacket)
var sockaddr: Sockaddr_storage
var sockaddrLen: SockLen
toSockAddr(ipPacket.ipAddrDst, ipPacket.tcpPortDst, sockaddr, sockaddrLen)
await rawFd.sendTo(packet.cstring, packet.len,
cast[ptr SockAddr](addr sockaddr), sockaddrLen)
echo &"injected {ipPacket.ipAddrSrc}:{ipPacket.tcpPortSrc.int} -> {ipPacket.ipAddrDst}:{ipPacket.tcpPortDst.int} (seq {ipPacket.tcpSeqNumber})"
except OSError as e:
echo &"cannot inject {ipPacket.ipAddrSrc}:{ipPacket.tcpPortSrc.int} -> {ipPacket.ipAddrDst}:{ipPacket.tcpPortDst.int} (seq {ipPacket.tcpSeqNumber})", e.msg
raise newException(PunchHoleError, e.msg)
proc captureSeqNumbers(puncher: TcpSyniPuncher, rawFd: AsyncFD,
cb: PunchProgressCb) {.async.} =
# FIXME: every sequence number is captured twice (RST too?)
@ -54,40 +68,41 @@ proc captureSeqNumbers(puncher: TcpSyniPuncher, rawFd: AsyncFD,
let packet = await rawFd.recv(4000)
if packet == "":
break
echo "packet len: ", packet.len
let parsed = parseEthernetPacket(packet)
if parsed.protocol == tcp and
parsed.tcpIpSrc == puncher.srcIp and
parsed.ipAddrSrc == puncher.srcIp and
parsed.tcpPortSrc.int == puncher.srcPort.int and
parsed.tcpIpDst == puncher.dstIp and
parsed.ipAddrDst == puncher.dstIp and
parsed.tcpFlags == {SYN}:
for i, port in puncher.dstPorts.pairs:
for port in puncher.dstPorts:
if parsed.tcpPortDst.int == port.int:
seqNums.add(parsed.tcpSeqNumber)
break
await cb(seqNums)
proc injectSyns(rawFd: AsyncFD, srcIp: IpAddress, srcPort: Port,
dstIp: IpAddress, dstPort: Port,
seqNums: seq[uint32]) {.async.} =
for seqNum in seqNums:
let ipPacket = IpPacket(protocol: tcp,
tcpIpSrc: srcIp,
tcpIpDst: dstIp,
tcpPortSrc: srcPort,
tcpPortDst: dstPort,
tcpSeqNumber: seqNum,
tcpFlags: {SYN})
try:
let packet = serialize(ipPacket)
var sockaddr: Sockaddr_storage
var sockaddrLen: SockLen
toSockAddr(dstIp, dstPort, sockaddr, sockaddrLen)
await rawFd.sendTo(packet.cstring, packet.len,
cast[ptr SockAddr](addr sockaddr), sockaddrLen)
echo &"injected {srcIP}:{srcPort.int} -> {dstIp}:{dstPort.int} (seq {seqNum})"
except OSError as e:
echo "cannot inject {srcIp}:{srcPort.int} -> {dstIp}:{dstPort.int} (seq {seqNum}): ", e.msg
proc captureAndResendSynAck(puncher: TcpSyniPuncher, rawFd: AsyncFD) {.async.} =
while true:
let packet = await rawFd.recv(4000)
if packet == "":
break
let parsed = parseEthernetPacket(packet)
if parsed.protocol == tcp and
parsed.ipAddrSrc == puncher.srcIp and
parsed.tcpPortSrc.int == puncher.srcPort.int and
parsed.ipAddrDst == puncher.dstIp and
parsed.tcpFlags == {SYN, ACK}:
for port in puncher.dstPorts:
if parsed.tcpPortDst.int == port.int:
let ipPacket = IpPacket(protocol: tcp,
ipAddrSrc: puncher.srcIp,
ipAddrDst: puncher.dstIp,
ipTTL: 64,
tcpPortSrc: puncher.srcPort,
tcpPortDst: parsed.tcpPortDst,
tcpSeqNumber: parsed.tcpSeqNumber,
tcpFlags: parsed.tcpFlags)
await rawFd.injectTcpPacket(ipPacket)
break
proc initPuncher*(srcPort: Port, dstIp: IpAddress, dstPorts: array[3, Port],
seqNums: seq[uint32] = @[]): TcpSyniPuncher =
@ -128,6 +143,7 @@ proc doConnect(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress, dstPort: Port,
sock.bindAddr(srcPort, $srcIp)
try:
await sock.connect($dstIp, dstPort)
sock.getFd.setSockOptInt(IPPROTO_IP, IP_TTL, 64)
future.complete(sock)
except OSError as e:
echo &"connection {srcIP}:{srcPort.int} -> {dstIp}:{dstPort.int} failed: ", e.msg
@ -141,8 +157,10 @@ proc connectParallel(puncher: TcpSyniPuncher): Future[AsyncSocket] =
proc connect*(puncher: TcpSyniPuncher,
progressCb: PunchProgressCb): Future[AsyncSocket] {.async.} =
let iface = fromIpAddress(puncher.srcIp)
let rawFd = setupEthernetCapturingSocket(iface)
asyncCheck puncher.captureSeqNumbers(rawFd, progressCb)
let captureSeqFd = setupEthernetCapturingSocket(iface)
let captureSynAckFd = setupEthernetCapturingSocket(iface)
asyncCheck puncher.captureSeqNumbers(captureSeqFd, progressCb)
asyncCheck puncher.captureAndResendSynAck(captureSynAckFd)
await puncher.addFirewallRules()
try:
result = await puncher.connectParallel()
@ -171,8 +189,16 @@ proc accept*(puncher: TcpSyniPuncher): Future[AsyncSocket] {.async.} =
# FIXME: timeout
let rawFd = setupTcpInjectingSocket()
for dstPort in puncher.dstPorts:
asyncCheck injectSyns(rawFd, puncher.dstIp, dstPort, puncher.srcIp,
puncher.srcPort, puncher.seqNums)
for seqNum in puncher.seqNums:
let ipPacket = IpPacket(protocol: tcp,
ipAddrSrc: puncher.dstIp,
ipAddrDst: puncher.srcIp,
ipTTL: 64,
tcpPortSrc: dstPort,
tcpPortDst: puncher.srcPort,
tcpSeqNumber: seqNum,
tcpFlags: {SYN})
asyncCheck rawFd.injectTcpPacket(ipPacket)
let sock = newAsyncSocket()
sock.setSockOpt(OptReuseAddr, true)
sock.bindAddr(puncher.srcPort, $(puncher.srcIp))