Simple Analysis and debugging of CVE-2015-7547 Overflow Vulnerability

Source: Internet
Author: User
Tags domain name server nameserver

Simple Analysis and debugging of CVE-2015-7547 Overflow Vulnerability

0x00 vulnerability information

Recently, glibc has a stack overflow vulnerability. For details about the vulnerability, refer to the following link.

CVE-2015-7547: glibc getaddrinfo stack-based buffer overflow

Poc on github: https://github.com/fjserna/CVE-2015-7547

0x01 environment preparation
Operating System: ubuntu15.04glibc version: glibc-2.2.0
1.1 glibc source code compilation

In ubuntu, you only need to execute the source code and the debug character command to use gdb to trace and debug glibc. The installation command is as follows:

sudo apt-get install libc6-dbgsudo apt-get source libc6-dev

But because the built-in glibc isReleaseSo the optimization Parameter-O2 is selected during compilation.The variable is optimized and cannot be read.And when the code is runningNumber of lines in source codeNo.

Therefore, you need to compile a glibc that is adjustable and not over-optimized for debugging.

First, download the glibc source code from the glibc official website. I chose version 2.20. The method for compiling and installing glibc can be easily found on the Internet. Note that some special parameters need to be set during configure. If you need to debug macros you can add-gdwarf-2, glibc cannot use-O0 compilation, but-O1 is enough.

/opt/glibc220/configure --prefix=/usr/local/glibc220/ --enable-debug CFLAGS="-g -O1" CPPFLAGS = "-g -O1"

After configure is executed, you only need to simply compile and install it.

sudo makesudo make install
1.2 use the debug version glibc to compile the POC

After glibc compilation and installation is successful, the system's default glibc is still the original one. Therefore, you need to select the specified glibc to compile the POC code.

gcc -o client CVE-2015-7547-client.c -Wl,-rpath /usr/local/glibc220

The ldd command shows that the newly compiled glibc is used.

At this time, you can use GDB to debug functions in glibc.

1.3 configure the local dns Server

Python server that runs poc. Modify/etc/resolv. conf. Change the Domain Name Server to 127.0.0.1. However, this machine will have problems accessing the network.

nameserver 127.0.0.1
0x02 Vulnerability Analysis 2.1 run POC

Use gdb to start the client and run it directly, causing a stack crash.

2.2 search for overflow Functions

We can see that all stacks are overwritten with 0x42424242. According to the analysis provided by google, the send_dg and send_vc functions are problematic. Run the send_vc and send_dg breakpoint to re-run the program. You will find that the send_dg function is called before the send_vc function is called.

It can be seen that the stack overflow occurred during send_vc.

According to the analysis provided by google, it can be seen that the socket overflow occurs when it is read. It can be analyzed by debugging with the source code. Remove unnecessary code. The core code is as follows, which involves four tasks in total.

[1] select the appropriate cache [2] Read the length of the dns package [3] Read The dsn package [4] to determine whether to read the second package.

 

#! Cstatic intsend_vc (res_state statp, const u_char * buf, int buflen, const u_char * buf2, int buflen2, u_char ** ansp, int * anssizp, int * terrno, int ns, u_char ** anscp, u_char ** ansp2, int * anssizp2, int * resplen2, int * ansp2_malloced) {const HEADER * hp = (HEADER *) buf; const HEADER * hp2 = (HEADER *) buf2; u_char * ans = * ansp; int orig_anssizp = * anssizp; [...] // you can ignore this task. Read_len: // ---------------- [2] --------------- start ---------------- cp = (u_char *) & rlen16; len = sizeof (rlen16 ); while (n = TEMP_FAILURE_RETRY (read (statp-> _ vcsock, cp, (int) len)> 0) {cp + = n; if (len-= n) <= 0) break;} if (n <= 0) {[...] // ignore error handling.} Int rlen = ntohs (rlen16); // ------------------ [2] --------------- end ---------------- // values [1] ------------- start starting int * values; u_char ** thisansp; int * values; if (recvresp1 | recvresp2) = 0 | buf2 = NULL) {// read the network package from read_len for the first time and enter this branch. Thisanssizp = anssizp; // memory available for the first call to read is 65536 thisansp = anscp?: Ansp; // The anscp assert (anscp! = NULL | ansp2 = NULL); thisresplenp = & resplen;} else {if (* anssizp! = MAXPACKET) {[...] // This will not be entered in the reproduction process.} Else {/* The first reply did not fit into the user-provided buffer. maybe the second answer will. */* anssizp2 = orig_anssizp; // The available memory length for the second call is 65536 * ansp2 = * ansp; // The cache ansp used for the second call to read} thisanssizp = anssizp2; thisansp = ansp2; response = resplen2;} // ---------------- [1] --------------- end ---------------- anhp = (HEADER *) * thisansp; * response = rlen; if (rlen> * response) {[...] // heavy This is not going into the current process.} Else len = rlen; if (_ glibc_unlikely (len <HFIXEDSZ) {[...] // This will not be entered during the reproduction process.} Cp = * thisansp; // * ansp; // ----------------- [2] ---------------------- start ------------------- while (len! = 0 & (n = read (statp-> _ vcsock, (char *) cp, (int) len)> 0) {// overflow point. Cp + = n; len-= n;} // ----------------- [2] -------------------- start ----------------- if (_ glibc_unlikely (n <= 0) {[...] // This part will not be entered during the reproduction process.} If (_ glibc_unlikely (truncating) {[...] // This will not be entered in the reproduction process. }/** If the calling application has led out of * a previous call and failed to arrange to have * the circuit closed or the server has got * itself confused, then drop the packet and * wait for the correct one. * // --------------- [4] ---------------------- start ----------------- if (recvresp1 | hp-> id! = Anhp-> id) // do not enter. & (Recvresp2 | hp2-> id! = Anhp-> id) {[...] // This will not be entered in the reproduction process. Goto read_len;}/* Mark which reply we have Ed. */if (recvresp1 = 0 & hp-> id = anhp-> id) // run recvresp1 = 1 recvresp2 = 0 recvresp1 = 1 for the first time; else recvresp2 = 1; /* Repeat waiting if we have a second answer to arrive. */if (recvresp1 & recvresp2) = 0) // call goto and return to the front. Goto read_len; // --------------- [4] ---------------------- end ---------------/** All is well, or the error is fatal. signal that the * next nameserver ought not be tried. */return resplen ;}

According to the source code analysis, when reading network packet data from the socket, it overflows, so the breakpoint is placed here.

gdb> b res_send.c:853

The call stack shows that read occurs twice [4], and is correct for the first time, and overflows after the second read. [1] indicates that the cp points to different memory during the two read calls.

When the read function is called for the first time, the buffer isAnscpMemory.

When the second read function is called, the buffer isAnspMemory.We do not need to consider the issue of second-level pointers for the time being.

We can conclude that the address of the ansp pointer index has a problem. Ansp is passed in from the parameter during the call. Therefore, you need to call the function by analyzing send_vc.

2.3 An error occurred while allocating memory.

The send_vc call function is as follows:

#! Cint _ libc_res_nsend (res_state statp, const u_char * buf, int buflen, const u_char * buf2, int buflen2, u_char * ans, int anssiz, u_char ** ansp, u_char ** ansp2, int * nansp2, int * resplen2, int * ansp2_malloced) {[...] if (_ glibc_unlikely (v_circuit) {/* Use VC; at most one attempt per server. */try = statp-> retry; n = send_vc (statp, buf, buflen, buf2, buflen2, // statp status, buff, bufflen the first group sends data, buff, 2 bu Fflen2 group 2 sends data. & Ans, & anssiz, & terrno, // u_char ** ansp, int * anssizp, int * terrno, ns, ansp, ansp2, nansp2, resplen2, // int ns, u_char ** anscp, u_char ** ansp2, int * anssizp2, int * resplen2, ansp2_malloced); // int * ansp2_malloced if (n <0) return (-1 ); if (n = 0 & (buf2 = NULL | * resplen2 = 0) goto next_ns;} else {/* Use limit rams. * /// called by the send_dg function, ansp points to 65536 buff, and ans points to 2048 buff. N = send_dg (statp, buf, buflen, buf2, buflen2, & ans, & anssiz, & terrno, ns, & v_circuit, & gotsomewhere, ansp, ansp2, nansp2, resplen2, ansp2_malloced); if (n <0) return (-1); if (n = 0 & (buf2 = NULL | * resplen2 = 0) goto next_ns; if (v_circuit) // XXX Check whether both requests failed or Z // XXX whether one has been answered successfully goto same_ns;} [...]}

Before send_vc is called, the program calls send_dg and the two function parameters are basically the same. by reading the source code, we can find that send_dg modifies the parameters and applies for new memory.

#! Cstatic intsend_dg (res_state statp, const u_char * buf, int buflen, const u_char * buf2, int buflen2, u_char ** ansp, int * anssizp, int * terrno, int ns, int * v_circuit, int * gotsomewhere, u_char ** anscp, u_char ** ansp2, int * anssizp2, int * resplen2, int * ansp2_malloced) {// ans points to a 2048-sized buffer // ansp points to ans // anscp points to ans const HEADER * hp = (HEADER *) buf; const HEADER * hp2 = (HEADER *) buf2; u_char * ans = * Nsp; int orig_anssizp = * anssizp; struct timespec now, timeout, finish; struct pollfd pfd [1]; int ptimeout; struct sockaddr_in6 from; int resplen = 0; int n; [...] else if (pfd [0]. revents & POLLIN) {int * thisanssizp; u_char ** thisansp; int * thisresplenp; if (recvresp1 | recvresp2) = 0 | buf2 = NULL) {// send_dg enters this branch for the first time. Thisanssizp = anssizp; thisansp = anscp?: Ansp; // thisansp is assigned anscp assert (anscp! = NULL | ansp2 = NULL); thisresplenp = & resplen;} else {[...] // The first call does not enter.} If (* thisanssizp <MAXPACKET/* Yes, we test ANSCP here. if we have two buffers both will be allocatable. */& anscp # ifdef FIONREAD & (ioctl (pfd [0]. fd, FIONREAD, thisresplenp) <0 | * thisanssizp <* thisresplenp) # endif) {u_char * newp = malloc (MAXPACKET); if (newp! = NULL) {* anssizp = MAXPACKET; // anssizp who points to the buffer of 65536 * thisansp = ans = newp; // anscp, however, ansp points to the original 2048 buffer if (thisansp = ansp2) * ansp2_malloced = 1 ;}}

Debugging shows that ansp still points to the buffer with the size of 2048, while anscp points to the buffer with the size of 65536. Then the two pointers are passed to send_vc.

2.4 cause of Overflow

The reason for the overflow is that * anssizp is assigned 65536 in the previous send_dg. When send_vc calls the read function for the second time, the ansp points to the buffer size of * anssizp, that is, 65536. In fact, ansp points to a buffer with only 2048 size. Therefore, stack overflow occurs after reading more than 2048 bytes from the socket.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.