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 URG
IpPacket* = object IpPacket* = object
ipAddrSrc*: IpAddress
ipAddrDst*: IpAddress
ipTTL*: uint8
case protocol*: Protocol case protocol*: Protocol
of tcp: of tcp:
tcpIpSrc*: IpAddress
tcpIpDst*: IpAddress
tcpPortSrc*: Port tcpPortSrc*: Port
tcpPortDst*: Port tcpPortDst*: Port
tcpSeqNumber*: uint32 tcpSeqNumber*: uint32
@ -87,8 +88,9 @@ proc parseEthernetPacket*(input: string): IpPacket =
let tcpHeader = cast[ptr Tcphdr](cast[int](ipHeader) + ipHeader.ip_hl.int * 4) let tcpHeader = cast[ptr Tcphdr](cast[int](ipHeader) + ipHeader.ip_hl.int * 4)
result = IpPacket(protocol: tcp, result = IpPacket(protocol: tcp,
tcpIpSrc: ipSrc, ipAddrSrc: ipSrc,
tcpIpDst: ipDst, ipAddrDst: ipDst,
ipTTL: ipHeader.ip_ttl,
tcpPortSrc: Port(ntohs(tcpHeader.th_sport)), tcpPortSrc: Port(ntohs(tcpHeader.th_sport)),
tcpPortDst: Port(ntohs(tcpHeader.th_dport)), tcpPortDst: Port(ntohs(tcpHeader.th_dport)),
tcpSeqNumber: ntohl(tcpHeader.th_seq), tcpSeqNumber: ntohl(tcpHeader.th_seq),
@ -132,8 +134,8 @@ proc serialize*(packet: IpPacket): string =
of tcp: of tcp:
result = newString(sizeof(Ip) + sizeof(Tcphdr)) result = newString(sizeof(Ip) + sizeof(Tcphdr))
zeroMem(result.cstring, result.len) zeroMem(result.cstring, result.len)
let srcIp = InAddr(s_addr: cast[uint32](packet.tcpIpSrc.address_v4)) let srcIp = InAddr(s_addr: cast[uint32](packet.ipAddrSrc.address_v4))
let dstIp = InAddr(s_addr: cast[uint32](packet.tcpIpDst.address_v4)) let dstIp = InAddr(s_addr: cast[uint32](packet.ipAddrDst.address_v4))
let ipHeader = cast[ptr Ip](addr result[0]) let ipHeader = cast[ptr Ip](addr result[0])
ipHeader.ip_hl = 5 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_len = htons(sizeof(Ip).uint16 + sizeof(TcpHdr).uint16)
ipHeader.ip_id = htons(54321) # FIXME = random number ipHeader.ip_id = htons(54321) # FIXME = random number
ipHeader.ip_off = 0 ipHeader.ip_off = 0
ipHeader.ip_ttl = 64 ipHeader.ip_ttl = packet.ipTTL
ipHeader.ip_p = 6.cuchar ipHeader.ip_p = 6.cuchar
ipHeader.ip_src = srcIp ipHeader.ip_src = srcIp
ipHeader.ip_dst = dstIp 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}" let firewall_cmd = fmt"iptables -D {chain} {rule}"
discard await asyncExecCmd(firewall_cmd) 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, proc captureSeqNumbers(puncher: TcpSyniPuncher, rawFd: AsyncFD,
cb: PunchProgressCb) {.async.} = cb: PunchProgressCb) {.async.} =
# FIXME: every sequence number is captured twice (RST too?) # 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) let packet = await rawFd.recv(4000)
if packet == "": if packet == "":
break break
echo "packet len: ", packet.len
let parsed = parseEthernetPacket(packet) let parsed = parseEthernetPacket(packet)
if parsed.protocol == tcp and if parsed.protocol == tcp and
parsed.tcpIpSrc == puncher.srcIp and parsed.ipAddrSrc == puncher.srcIp and
parsed.tcpPortSrc.int == puncher.srcPort.int and parsed.tcpPortSrc.int == puncher.srcPort.int and
parsed.tcpIpDst == puncher.dstIp and parsed.ipAddrDst == puncher.dstIp and
parsed.tcpFlags == {SYN}: parsed.tcpFlags == {SYN}:
for i, port in puncher.dstPorts.pairs: for port in puncher.dstPorts:
if parsed.tcpPortDst.int == port.int: if parsed.tcpPortDst.int == port.int:
seqNums.add(parsed.tcpSeqNumber) seqNums.add(parsed.tcpSeqNumber)
break break
await cb(seqNums) await cb(seqNums)
proc injectSyns(rawFd: AsyncFD, srcIp: IpAddress, srcPort: Port, proc captureAndResendSynAck(puncher: TcpSyniPuncher, rawFd: AsyncFD) {.async.} =
dstIp: IpAddress, dstPort: Port, while true:
seqNums: seq[uint32]) {.async.} = let packet = await rawFd.recv(4000)
for seqNum in seqNums: if packet == "":
let ipPacket = IpPacket(protocol: tcp, break
tcpIpSrc: srcIp, let parsed = parseEthernetPacket(packet)
tcpIpDst: dstIp, if parsed.protocol == tcp and
tcpPortSrc: srcPort, parsed.ipAddrSrc == puncher.srcIp and
tcpPortDst: dstPort, parsed.tcpPortSrc.int == puncher.srcPort.int and
tcpSeqNumber: seqNum, parsed.ipAddrDst == puncher.dstIp and
tcpFlags: {SYN}) parsed.tcpFlags == {SYN, ACK}:
try: for port in puncher.dstPorts:
let packet = serialize(ipPacket) if parsed.tcpPortDst.int == port.int:
var sockaddr: Sockaddr_storage let ipPacket = IpPacket(protocol: tcp,
var sockaddrLen: SockLen ipAddrSrc: puncher.srcIp,
toSockAddr(dstIp, dstPort, sockaddr, sockaddrLen) ipAddrDst: puncher.dstIp,
await rawFd.sendTo(packet.cstring, packet.len, ipTTL: 64,
cast[ptr SockAddr](addr sockaddr), sockaddrLen) tcpPortSrc: puncher.srcPort,
echo &"injected {srcIP}:{srcPort.int} -> {dstIp}:{dstPort.int} (seq {seqNum})" tcpPortDst: parsed.tcpPortDst,
except OSError as e: tcpSeqNumber: parsed.tcpSeqNumber,
echo "cannot inject {srcIp}:{srcPort.int} -> {dstIp}:{dstPort.int} (seq {seqNum}): ", e.msg tcpFlags: parsed.tcpFlags)
await rawFd.injectTcpPacket(ipPacket)
break
proc initPuncher*(srcPort: Port, dstIp: IpAddress, dstPorts: array[3, Port], proc initPuncher*(srcPort: Port, dstIp: IpAddress, dstPorts: array[3, Port],
seqNums: seq[uint32] = @[]): TcpSyniPuncher = seqNums: seq[uint32] = @[]): TcpSyniPuncher =
@ -128,6 +143,7 @@ proc doConnect(srcIp: IpAddress, srcPort: Port, dstIp: IpAddress, dstPort: Port,
sock.bindAddr(srcPort, $srcIp) sock.bindAddr(srcPort, $srcIp)
try: try:
await sock.connect($dstIp, dstPort) await sock.connect($dstIp, dstPort)
sock.getFd.setSockOptInt(IPPROTO_IP, IP_TTL, 64)
future.complete(sock) future.complete(sock)
except OSError as e: except OSError as e:
echo &"connection {srcIP}:{srcPort.int} -> {dstIp}:{dstPort.int} failed: ", e.msg 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, proc connect*(puncher: TcpSyniPuncher,
progressCb: PunchProgressCb): Future[AsyncSocket] {.async.} = progressCb: PunchProgressCb): Future[AsyncSocket] {.async.} =
let iface = fromIpAddress(puncher.srcIp) let iface = fromIpAddress(puncher.srcIp)
let rawFd = setupEthernetCapturingSocket(iface) let captureSeqFd = setupEthernetCapturingSocket(iface)
asyncCheck puncher.captureSeqNumbers(rawFd, progressCb) let captureSynAckFd = setupEthernetCapturingSocket(iface)
asyncCheck puncher.captureSeqNumbers(captureSeqFd, progressCb)
asyncCheck puncher.captureAndResendSynAck(captureSynAckFd)
await puncher.addFirewallRules() await puncher.addFirewallRules()
try: try:
result = await puncher.connectParallel() result = await puncher.connectParallel()
@ -171,8 +189,16 @@ proc accept*(puncher: TcpSyniPuncher): Future[AsyncSocket] {.async.} =
# FIXME: timeout # FIXME: timeout
let rawFd = setupTcpInjectingSocket() let rawFd = setupTcpInjectingSocket()
for dstPort in puncher.dstPorts: for dstPort in puncher.dstPorts:
asyncCheck injectSyns(rawFd, puncher.dstIp, dstPort, puncher.srcIp, for seqNum in puncher.seqNums:
puncher.srcPort, 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() let sock = newAsyncSocket()
sock.setSockOpt(OptReuseAddr, true) sock.setSockOpt(OptReuseAddr, true)
sock.bindAddr(puncher.srcPort, $(puncher.srcIp)) sock.bindAddr(puncher.srcPort, $(puncher.srcIp))