From acfcb79108ebd9f0d98a5ed6202f0614e1980939 Mon Sep 17 00:00:00 2001 From: Christian Ulrich Date: Sun, 23 Aug 2020 14:36:19 +0200 Subject: [PATCH] resend SYN+ACK with normal TTL --- ip_packet.nim | 16 +++++----- tcp_syni.nim | 84 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 64 insertions(+), 36 deletions(-) diff --git a/ip_packet.nim b/ip_packet.nim index 2794566..0649204 100644 --- a/ip_packet.nim +++ b/ip_packet.nim @@ -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 diff --git a/tcp_syni.nim b/tcp_syni.nim index dcfa061..f3be199 100644 --- a/tcp_syni.nim +++ b/tcp_syni.nim @@ -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))