qiling
qiling copied to clipboard
Meet gevent exception
*Describe the bug when simulating proftpd-1.3.0, i meet a bug.
Sample Code
ql = Qiling(["proftpd", "-n"], multithread=True)
ql.run()
ERROR
Traceback (most recent call last):
File "1.py", line 13, in
Additional context i notice that you have comment just next to the bug:
If we get exceptions from gevent here, it means a critical bug related to multithread.
Please fire an issue if you encounter an exception from gevent.
SO HOW TO FIX IT? THX!
Log && Reproduction binary.
Log && Reproduction binary.
hi, i add some log operations and rebuild the binary. then i rerun it in 'no-daemon' mode and 'daemon' mode.
when i run proftpd in 'daemon' mode, it will daemonize itself: clone(with syscall fork) a new process(in simulator it is thread 8143) and terminate the old 'process' / thread(2000). after the old 'process' exit, the new 'process' runs into a daemon loop to select IO. this is the position where new process throws the exception.

in terms of 'no-daemon' mode, the situation is similar. proftpd will start loop in thread 2000, and block at syscall select. at this time the log is like:

when i try 'ftp 127.0.0.1 8021' to connect this server process, it breaks down.

client terminal is like:

thank you very much for your answer, i will provide more information if necessary. keep trying....
static void daemon_loop(void) {
fd_set listenfds;
conn_t *listen_conn;
int fd, maxfd;
int i, err_count = 0;
unsigned long nconnects = 0UL;
time_t last_error;
struct timeval tv;
static int running = 0;
set_proc_title("(accepting connections)");
time(&last_error);
while (TRUE) {
pr_log_pri(PR_LOG_ERR, "[yyt] daemon loop");
run_schedule();
FD_ZERO(&listenfds);
maxfd = 0;
maxfd = pr_ipbind_listen(&listenfds);
/* Monitor children pipes */
maxfd = semaphore_fds(&listenfds, maxfd);
/* Check for ftp shutdown message file */
switch (check_shutmsg(&shut, &deny, &disc, shutmsg, sizeof(shutmsg))) {
case 1:
if (!shutdownp)
disc_children();
shutdownp = 1;
break;
case 0:
shutdownp = 0;
deny = disc = (time_t) 0;
break;
}
pr_log_pri(PR_LOG_ERR, "[yyt] shutdownp");
if (shutdownp) {
tv.tv_sec = 5L;
tv.tv_usec = 0L;
} else {
tv.tv_sec = PR_TUNABLE_SELECT_TIMEOUT;
tv.tv_usec = 0L;
}
/* If running (a flag signaling whether proftpd is just starting up)
* AND shutdownp (a flag signalling the present of /etc/shutmsg) are
* true, then log an error stating this -- but don't stop the server.
*/
pr_log_pri(PR_LOG_ERR, "[yyt] shutdownp && !running");
if (shutdownp && !running) {
/* Check the value of the deny time_t struct w/ the current time.
* If the deny time has passed, log that all incoming connections
* will be refused. If not, note the date at which they will be
* refused in the future.
*/
time_t now = time(NULL);
if (difftime(deny, now) < 0.0) {
pr_log_pri(PR_LOG_ERR, PR_SHUTMSG_PATH
" present: all incoming connections will be refused.");
} else {
pr_log_pri(PR_LOG_ERR, PR_SHUTMSG_PATH " present: incoming connections "
"will be denied starting %s", CHOP(ctime(&deny)));
}
}
running = 1;
pr_log_pri(PR_LOG_ERR, "[yyt] before syscall: select");
i = select(maxfd + 1, &listenfds, NULL, NULL, &tv);
pr_log_pri(PR_LOG_ERR, "[yyt] after syscall: select");
if (i == -1 && errno == EINTR) {
pr_signals_handle();
continue;
}
if (have_dead_child) {
sigset_t sig_set;
sigemptyset(&sig_set);
sigaddset(&sig_set, SIGCHLD);
sigaddset(&sig_set, SIGTERM);
pr_alarms_block();
sigprocmask(SIG_BLOCK, &sig_set, NULL);
have_dead_child = FALSE;
child_update();
sigprocmask(SIG_UNBLOCK, &sig_set, NULL);
pr_alarms_unblock();
}
if (i == -1) {
time_t this_error;
time(&this_error);
if ((this_error - last_error) <= 5 && err_count++ > 10) {
pr_log_pri(PR_LOG_ERR, "Fatal: select() failing repeatedly, shutting "
"down.");
exit(1);
} else if ((this_error - last_error) > 5) {
last_error = this_error;
err_count = 0;
}
pr_log_pri(PR_LOG_NOTICE, "select() failed in daemon_loop(): %s",
strerror(errno));
}
if (i == 0)
continue;
/* Reset the connection counter. Take into account this current
* connection, which does not (yet) have an entry in the child list.
*/
nconnects = 1UL;
/* See if child semaphore pipes have signaled */
if (child_count()) {
pr_child_t *ch;
time_t now = time(NULL);
for (ch = child_get(NULL); ch; ch = child_get(ch)) {
if (ch->ch_pipefd != -1 &&
FD_ISSET(ch->ch_pipefd, &listenfds)) {
close(ch->ch_pipefd);
ch->ch_pipefd = -1;
}
/* While we're looking, tally up the number of children forked in
* the past interval.
*/
if (ch->ch_when >= (now - (unsigned long) max_connect_interval))
nconnects++;
}
}
pr_signals_handle();
/* Accept the connection. */
listen_conn = pr_ipbind_accept_conn(&listenfds, &fd);
/* Fork off servers to handle each connection our job is to get back to
* answering connections asap, so leave the work of determining which
* server the connection is for to our child.
*/
if (listen_conn) {
/* Check for exceeded MaxInstances. */
if (ServerMaxInstances && (child_count() >= ServerMaxInstances)) {
pr_event_generate("core.max-instances", NULL);
pr_log_pri(PR_LOG_WARNING,
"MaxInstances (%d) reached, new connection denied",
ServerMaxInstances);
close(fd);
/* Check for exceeded MaxConnectionRate. */
} else if (max_connects && (nconnects > max_connects)) {
pr_event_generate("core.max-connection-rate", NULL);
pr_log_pri(PR_LOG_WARNING,
"MaxConnectionRate (%lu/%u secs) reached, new connection denied",
max_connects, max_connect_interval);
close(fd);
/* Fork off a child to handle the connection. */
} else {
pr_log_pri(PR_LOG_ERR, "[yyt] before fork_server(fd, listen_conn, FALSE);");
fork_server(fd, listen_conn, FALSE);
pr_log_pri(PR_LOG_ERR, "[yyt] parent: after fork_server(fd, listen_conn, FALSE);");
}
}
#ifdef PR_DEVEL_NO_DAEMON
/* Do not continue the while() loop here if not daemonizing. */
break;
#endif /* PR_DEVEL_NO_DAEMON */
}
}
I wasn't able to find the executable online (only a recent, non vulnerable, version of it). Would you mind sharing your rootfs directory with all the relevant binaries and libs inside to let us reproduce?
I wasn't able to find the executable online (only a recent, non vulnerable, version of it). Would you mind sharing your rootfs directory with all the relevant binaries and libs inside to let us reproduce?
Sorry for the late reply! Sure! i get the binaries from this url: https://drive.google.com/file/d/0B_6p5h2gdgmoTV9ON0xYZWpMRTg/view, you can run /usr/local/proftpd-1.3.0/sbin/proftpd. but i think maybe its better to provide you all my rootfs directory. if you want so, could you give me an email address please? i ll pack it and send to you ;-) keep contact
The access to link is denied.
Please grant the necessary access, and share your rootfs directory content (preferably as a tar.gz file).
Close for now.
We updated the codebase for Qiling and Unicorn since this issue being posted.
Feel free to try the latest version.